home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / win32.c < prev    next >
C/C++ Source or Header  |  1996-06-12  |  74KB  |  3,361 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  * win32.c
  11.  *
  12.  * Win32 (Windows NT and Windows 95) system-dependent routines.
  13.  * Portions lifted from the Win32 SDK samples, the MSDOS-dependent code,
  14.  * NetHack 3.1.3, GNU Emacs 19.30, and Vile 5.5.
  15.  *
  16.  * George V. Reilly <gvr@halcyon.com> wrote most of this.
  17.  * Roger Knobbe <rogerk@wonderware.com> did the initial port of Vim 3.0.
  18.  */
  19.  
  20. #include <io.h>
  21. #include "vim.h"
  22. #include "globals.h"
  23. #include "option.h"
  24. #include "proto.h"
  25. #ifdef HAVE_FCNTL_H
  26. # include <fcntl.h>
  27. #endif
  28. #include <sys/types.h>
  29. #include <errno.h>
  30. #include <signal.h>
  31. #include <limits.h>
  32. #include <process.h>
  33. #include <time.h>
  34.  
  35. #define STRICT
  36. #define WIN32_LEAN_AND_MEAN
  37. #include <windows.h>
  38.  
  39. #undef chdir
  40. #include <direct.h>
  41.  
  42.  
  43. /* Disgusting hack for getting dead keys to work properly on Windows 95.
  44.  * See below for the gory details.  If you're only running on NT or
  45.  * if you're an English-speaking user, you can comment this out. */
  46.  
  47. /* WARNING: this code is experimental and incomplete and it does not work.
  48.  * Don't use it! */
  49. /* #define WIN95_DEAD_KEYS_HACK */
  50.  
  51.  
  52. /* Force all filenames to lowercase */
  53. /* #define DOWNCASE_FILENAMES */
  54.  
  55.  
  56. /* Record all output and all keyboard & mouse input */
  57. /* #define MCH_WRITE_DUMP */
  58.  
  59. #ifdef MCH_WRITE_DUMP
  60. FILE* fdDump = NULL;
  61. #endif /* MCH_WRITE_DUMP */
  62.  
  63.  
  64. /*
  65.  * When generating prototypes for Win32 on Unix, these lines make the syntax
  66.  * errors disappear.  They do not need to be correct.
  67.  */
  68. #ifdef PROTO
  69. # define HANDLE int
  70. # define SMALL_RECT int
  71. # define COORD int
  72. # define SHORT int
  73. # define WORD int
  74. # define DWORD int
  75. # define BOOL int
  76. # define LPSTR int
  77. # define KEY_EVENT_RECORD int
  78. # define MOUSE_EVENT_RECORD int
  79. # define WINAPI
  80. # define CONSOLE_CURSOR_INFO int
  81. # define LPCSTR char_u *
  82. #endif
  83.  
  84. /* Win32 Console handles for input and output */
  85. static HANDLE g_hConIn  = INVALID_HANDLE_VALUE;
  86. static HANDLE g_hSavOut = INVALID_HANDLE_VALUE;
  87. static HANDLE g_hCurOut = INVALID_HANDLE_VALUE;
  88. static HANDLE g_hConOut = INVALID_HANDLE_VALUE;
  89.  
  90. /* Win32 Screen buffer,coordinate,console I/O information */
  91. static SMALL_RECT g_srScrollRegion;
  92. static COORD      g_coord;    /* 0-based, but external coords are 1-based */
  93.  
  94. /* The attribute of the screen when the editor was started */
  95. static WORD  g_attrDefault = 7;  /* lightgray text on black background */
  96. static WORD  g_attrCurrent;
  97.  
  98. static int g_fCBrkPressed = FALSE;  /* set by ctrl-break interrupt */
  99. static int g_fCtrlCPressed = FALSE; /* set when ctrl-C or ctrl-break detected */
  100.  
  101. static void termcap_mode_start();
  102. static void termcap_mode_end();
  103. static void clear_chars(COORD coord, DWORD n);
  104. static void clear_screen();
  105. static void clear_to_end_of_display();
  106. static void clear_to_end_of_line();
  107. static void scroll(unsigned cLines);
  108. static void set_scroll_region(unsigned left, unsigned top,
  109.                               unsigned right, unsigned bottom);
  110. static void insert_lines(unsigned cLines);
  111. static void delete_lines(unsigned cLines);
  112. static void gotoxy(unsigned x, unsigned y);
  113. static void normvideo();
  114. static void textattr(WORD wAttr);
  115. static void standout();
  116. static void standend();
  117. static void visual_bell();
  118. static void cursor_visible(BOOL fVisible);
  119. static BOOL write_chars(LPCSTR pchBuf, DWORD cchToWrite, DWORD* pcchWritten);
  120.  
  121.  
  122. /* This symbol is not defined in older versions of the SDK or Visual C++ */
  123.  
  124. #ifndef VER_PLATFORM_WIN32_WINDOWS
  125. # define VER_PLATFORM_WIN32_WINDOWS 1
  126. #endif
  127.  
  128. static DWORD g_PlatformId;
  129.  
  130. /*
  131.  *    Returns VER_PLATFORM_WIN32_NT (NT) or VER_PLATFORM_WIN32_WINDOWS (Win95)
  132.  */
  133.     static DWORD
  134. PlatformId()
  135. {
  136.     OSVERSIONINFO ovi;
  137.  
  138.     ovi.dwOSVersionInfoSize = sizeof(ovi);
  139.     GetVersionEx(&ovi);
  140.  
  141.     g_PlatformId = ovi.dwPlatformId;
  142.  
  143.     return g_PlatformId;
  144. }
  145.  
  146.  
  147.  
  148. #define SHIFT  (SHIFT_PRESSED)
  149. #define CTRL   (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)
  150. #define ALT    (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED)
  151. #define ALT_GR (RIGHT_ALT_PRESSED  | LEFT_CTRL_PRESSED) 
  152.  
  153.  
  154.  
  155. #ifdef WIN95_DEAD_KEYS_HACK
  156.  
  157. /* The problem that this code attempts to work around is that the
  158.  * console-mode input routines are broken on Windows 95 if you're
  159.  * using dead keys.  A dead key is an accent key, such as acute,
  160.  * grave, or umlaut, that doesn't produce a character by itself,
  161.  * but when followed by another key, produces an accented character,
  162.  * such as a-acute, e-grave, u-umlaut, n-tilde, and so on.  Very
  163.  * useful for most European languages.  English-language keyboard
  164.  * layouts don't use dead keys, as far as I know.
  165.  *
  166.  * Unfortunately, the KEY_EVENT_RECORD contains the wrong data when
  167.  * I get the keystroke that's supposed to have an accented character.
  168.  * The AsciiChar part contains the unaccented character instead.
  169.  *
  170.  * The following code (unsuccessfully) attempts to fake the dead keys.
  171.  * I rely on the fact that the virtual key code and the scan key code
  172.  * are actually correct for both the dead key and the following letter.
  173.  * I set up a second thread that creates a hidden window and then sits
  174.  * in a little message loop.  I send it all the data from the dead key
  175.  * and the following key (each thread has its own keyboard input state),
  176.  * and it sends its own hidden window a suitable sequence of WM_KEYDOWN
  177.  * and WM_KEYUP messages.  TranslateMessage causes these to be turned
  178.  * into WM_DEADCHAR and WM_CHAR messages.  Almost.  It works as long as
  179.  * neither the dead key nor the following key are shifted, but after
  180.  * happens, that it goes to hell in a handbasket.
  181.  *
  182.  * I have spent many, many hours trying all sorts of approaches to get
  183.  * dead keys to work with Windows 95 and I am completely stumped.
  184.  *
  185.  * So why not just have all the input come directly via WM_CHAR?
  186.  * Because this is a console application and I don't know how to do that.
  187.  *
  188.  * So how do you type dead keys?  Answer: you don't, not if you're using
  189.  * Windows 95, not until either Microsoft fixes the bug (and they haven't
  190.  * as of Service Pack 1) or someone (me?) produces a true Windows GUI
  191.  * version of Vim.
  192.  *
  193.  * If someone can come up with some workaround, please, please, please
  194.  * send it to me.
  195.  *
  196.  * You can, however, use digraphs.  They're not as convenient as dead
  197.  * keys, especially if you're used to typing dead keys, but they do
  198.  * work and they're better than nothing.
  199.  *
  200.  * If you're using NT, the dead keys work fine.  At least they do with
  201.  * NT 3.51, Service Pack 3, US Edition.  I haven't tested it with any
  202.  * other version of NT.  I did hear one report that NT3.51, SP3, UK edition
  203.  * had problems with CTRL-vowels, but I don't know about dead keys.
  204.  *
  205.  * /George V. Reilly, 3/15/1996
  206.  *
  207.  * On 24th March, I was told by a senior developer at Microsoft:
  208.  *   Win95 console support has always been and will always be flaky.
  209.  *   
  210.  *   The flakiness is unavoidable because we are stuck between the world of
  211.  *   MS-DOS keyboard TSRs like KEYB (which wants to cook the data; important
  212.  *   for international) and the world of Win32.
  213.  *   
  214.  *   So keys that don't "exist" in MS-DOS land (like dead keys) have a
  215.  *   very tenuous existence in Win32 console land.  Keys that act
  216.  *   differently between MS-DOS land and Win32 console land (like capslock)
  217.  *   will act flaky.
  218.  *   
  219.  *   Don't even *mention* the problems with multiple language keyboard
  220.  *   layouts...
  221.  */
  222.  
  223. static HWND   g_hwndKbd  = NULL;
  224. static HANDLE g_hthrdKbd = NULL;
  225. static DWORD  g_dwThreadId = 0;
  226.  
  227. #define WM_SENDDEADKEY (WM_USER+31)
  228.  
  229.  
  230. void send_key(UINT uVirtKey, UINT uScanCode, UINT fDown);
  231. void key_down(UINT uVirtKey, UINT uScanCode);
  232. void keystate_down(UINT uCtrlState);
  233. void key_up(UINT uVirtKey, UINT uScanCode);
  234. void keystate_up(UINT uCtrlState);
  235.  
  236.     static LRESULT CALLBACK
  237. keyboard_wndproc(
  238.     HWND hwnd,
  239.     UINT uMsg,
  240.     WPARAM wParam,
  241.     LPARAM lParam)
  242. {
  243.     static int n, vk, sc; 
  244.     char ch, sz[200];
  245.  
  246.     sprintf(sz, "kbd: msg = %04x, w = %08x, l = %08x\n", uMsg, wParam, lParam);
  247.     OutputDebugString(sz);
  248.  
  249.     switch (uMsg)
  250.     {
  251.     case WM_SENDDEADKEY:
  252.         {
  253.             UINT uDeadVirt, uDeadScan, uDeadCtrl, uVirt, uScan, uCtrl;
  254.             BYTE abKeystate[256];
  255.             WORD awAnsiCode[2];
  256.             
  257.             uDeadVirt = LOBYTE(wParam);
  258.             uDeadScan = HIBYTE(wParam);
  259.             uDeadCtrl = HIWORD(wParam);
  260.  
  261.             uVirt = LOBYTE(lParam);
  262.             uScan = HIBYTE(lParam);
  263.             uCtrl = HIWORD(lParam);
  264.  
  265.             GetKeyboardState(abKeystate);
  266.             
  267.             memset(abKeystate, 0, sizeof (abKeystate));
  268.             if (!SetKeyboardState(abKeystate))
  269.             {
  270.                 n = GetLastError();
  271.                 sprintf(sz, "setkeyboardstate failed, error %d\n", n);
  272.                 OutputDebugString(sz);
  273.             }
  274.  
  275.             ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0),
  276.                     abKeystate, awAnsiCode, 0);
  277.     
  278.             if (uDeadCtrl & SHIFT_PRESSED) 
  279.                 abKeystate[VK_SHIFT] = 0x80;
  280.             if (uDeadCtrl & CAPSLOCK_ON) 
  281.                 abKeystate[VK_CAPITAL] = 1;
  282.             
  283.             if ((uDeadCtrl & ALT_GR) == ALT_GR)
  284.             {
  285.                 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
  286.                     abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
  287.             }
  288.             
  289.             if (!SetKeyboardState(abKeystate))
  290.             {
  291.                 n = GetLastError();
  292.                 sprintf(sz, "setkeyboardstate failed, error %d\n", n);
  293.                 OutputDebugString(sz);
  294.             }
  295.  
  296.             //keystate_down(uDeadCtrl);
  297.             key_down(uDeadVirt, uDeadScan);
  298.             key_up(uDeadVirt, uDeadScan);
  299.             //keystate_up(uDeadCtrl);
  300.  
  301.             memset(abKeystate, 0, sizeof (abKeystate));
  302.     
  303.             if (uCtrl & SHIFT_PRESSED) 
  304.                 abKeystate[VK_SHIFT] = 0x80;
  305.             if (uCtrl & CAPSLOCK_ON) 
  306.                 abKeystate[VK_CAPITAL] = 1;
  307.             
  308.             if ((uCtrl & ALT_GR) == ALT_GR)
  309.             {
  310.                 abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
  311.                     abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
  312.             }
  313.             
  314.             if (!SetKeyboardState(abKeystate))
  315.             {
  316.                 int n = GetLastError();
  317.             }
  318.  
  319.             //keystate_down(uCtrl);
  320.             key_down(uVirt, uScan);
  321.             Sleep(30);
  322.  
  323.             key_up(uVirt, uScan);
  324.             //keystate_up(uCtrl);
  325.         }
  326.         break;
  327.         
  328.     case WM_CHAR:
  329.     case WM_DEADCHAR:
  330.         ch = wParam;
  331.         CharToOemBuff(&ch, &ch, 1);
  332.         sprintf(sz, "WM_%sCHAR: wParam = 0x%2x, Oem = 0x%2x, '%c'\n",
  333.                 ((uMsg == WM_CHAR) ? "" : "DEAD"),
  334.                 wParam, LOBYTE(ch), isprint(ch) ? ch : '?');
  335.         OutputDebugString(sz);
  336.  
  337.         n = DefWindowProc(hwnd, uMsg, wParam, lParam);
  338.         return n;
  339.  
  340.     case WM_KEYDOWN:
  341.         vk = wParam;
  342.         sc = HIWORD(lParam);
  343.  
  344.     default:
  345.         return DefWindowProc(hwnd, uMsg, wParam, lParam);
  346.     }
  347. }
  348.  
  349.  
  350.  
  351.     static DWORD WINAPI
  352. keyboard_threadproc(
  353.     LPVOID lpvThreadParam)
  354. {
  355.     const char szKbdTrans[] = "VimKbdXlator";
  356.     WNDCLASS wndclass;
  357.     MSG msg;
  358.     int i = 0;
  359.     
  360.     wndclass.style = 0;
  361.     wndclass.lpfnWndProc = keyboard_wndproc;
  362.     wndclass.cbClsExtra = 0;
  363.     wndclass.cbWndExtra = 0;
  364.     wndclass.hInstance = GetModuleHandle(NULL);
  365.     wndclass.hIcon = NULL;
  366.     wndclass.hCursor = NULL;
  367.     wndclass.hbrBackground = NULL;
  368.     wndclass.lpszMenuName = NULL;
  369.     wndclass.lpszClassName = szKbdTrans;
  370.  
  371.     RegisterClass(&wndclass);
  372.     
  373.     g_hwndKbd = CreateWindow(szKbdTrans, "", 0,
  374.                              CW_USEDEFAULT, CW_USEDEFAULT,
  375.                              CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
  376.                              GetModuleHandle(NULL), NULL);
  377.     
  378.     while (GetMessage(&msg, NULL, 0, 0))
  379.     {
  380. #if 0
  381.         char sz[200];
  382.         sprintf(sz, "gm: %2d: hwnd = %x, msg = %x, w = %x, l = %x\n",
  383.                 ++i, msg.hwnd, msg.message, msg.wParam, msg.lParam);
  384.         OutputDebugString(sz);
  385. #endif
  386.  
  387.         TranslateMessage(&msg);
  388.         DispatchMessage(&msg);
  389.     }
  390.  
  391.     return msg.wParam;
  392. }
  393.  
  394.  
  395.  
  396.     static BOOL
  397. keyboard_init()
  398. {
  399.     const char sz[] = "aA\xE0\xE1\xE8\xE9\xF1\xD1";
  400.     const char* psz = sz;
  401.  
  402.     for ( ; *psz; psz++)
  403.     {
  404.         char sz2[100];
  405.         SHORT w = VkKeyScan(*psz);
  406.         
  407.         sprintf(sz2, "%02x: %04x\n", *psz, w);
  408.         OutputDebugString(sz2);
  409.     }
  410.  
  411.     g_hthrdKbd = (HANDLE) _beginthreadex(NULL, 0, keyboard_threadproc,
  412.                                          (LPVOID) NULL, 0, &g_dwThreadId);
  413.  
  414.     return (g_hthrdKbd != NULL);
  415. }
  416.  
  417.  
  418.  
  419.     static void
  420. keyboard_exit()
  421. {
  422.     TerminateThread(g_hthrdKbd, 0);
  423. }
  424.  
  425.  
  426.  
  427.     void
  428. send_key(
  429.     UINT uVirtKey,
  430.     UINT uScanCode,
  431.     UINT fDown)
  432. {
  433.     PostMessage(g_hwndKbd,
  434.                 fDown ? WM_KEYDOWN : WM_KEYUP,
  435.                 uVirtKey,
  436.                 MAKELONG(1, uScanCode | (fDown ? 0 : 0x8000)));
  437.     Sleep(20);
  438. }
  439.  
  440.  
  441.  
  442.     void
  443. key_down(
  444.     UINT uVirtKey,
  445.     UINT uScanCode)
  446. {
  447.     send_key(uVirtKey, uScanCode, TRUE);
  448. }
  449.  
  450.  
  451.  
  452.     void
  453. keystate_down(
  454.     UINT uCtrlState)
  455. {
  456.     if (uCtrlState & SHIFT_PRESSED)
  457.         key_down(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0));
  458.     if ((uCtrlState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0)
  459.         key_down(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0));
  460. }
  461.  
  462.  
  463.  
  464.     void
  465. key_up(
  466.     UINT uVirtKey,
  467.     UINT uScanCode)
  468. {
  469.     send_key(uVirtKey, uScanCode, FALSE);
  470. }
  471.  
  472.  
  473.  
  474.     void
  475. keystate_up(
  476.     UINT uCtrlState)
  477. {
  478.     if ((uCtrlState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED)) != 0)
  479.         key_up(VK_CONTROL, MapVirtualKey(VK_CONTROL, 0));
  480.     if (uCtrlState & SHIFT_PRESSED)
  481.         key_up(VK_SHIFT, MapVirtualKey(VK_SHIFT, 0));
  482. }
  483.  
  484. #endif /* WIN95_DEAD_KEYS_HACK */
  485.  
  486.  
  487.  
  488. /* When uChar.AsciiChar is 0, then we need to look at wVirtualKeyCode.
  489.  * We map function keys to their ANSI terminal equivalents, as produced
  490.  * by ANSI.SYS, for compatibility with the MS-DOS version of Vim.  Any
  491.  * ANSI key with a value >= '\300' is nonstandard, but provided anyway
  492.  * so that the user can have access to all SHIFT-, CTRL-, and ALT-
  493.  * combinations of function/arrow/etc keys.
  494.  */
  495.  
  496. const static struct {
  497.     WORD    wVirtKey;
  498.     BOOL    fAnsiKey;
  499.     int        chAlone;
  500.     int        chShift;
  501.     int        chCtrl;
  502.     int        chAlt;
  503. } VirtKeyMap[] = {
  504.  
  505. /*      Key        ANSI    alone    shift    ctrl        alt */
  506.     { VK_ESCAPE,FALSE,    ESC,    ESC,    ESC,        ESC,    },
  507.  
  508.     { VK_F1,    TRUE,    ';',    'T',    '^',        'h', },
  509.     { VK_F2,    TRUE,    '<',    'U',    '_',        'i', },
  510.     { VK_F3,    TRUE,    '=',    'V',    '`',        'j', },
  511.     { VK_F4,    TRUE,    '>',    'W',    'a',        'k', },
  512.     { VK_F5,    TRUE,    '?',    'X',    'b',        'l', },
  513.     { VK_F6,    TRUE,    '@',    'Y',    'c',        'm', },
  514.     { VK_F7,    TRUE,    'A',    'Z',    'd',        'n', },
  515.     { VK_F8,    TRUE,    'B',    '[',    'e',        'o', },
  516.     { VK_F9,    TRUE,    'C',    '\\',    'f',        'p', },
  517.     { VK_F10,    TRUE,    'D',    ']',    'g',        'q', },
  518.     { VK_F11,    TRUE,    '\205',    '\207',    '\211',        '\213', },
  519.     { VK_F12,    TRUE,    '\206',    '\210',    '\212',        '\214', },
  520.  
  521.     { VK_HOME,    TRUE,    'G',    '\302',    'w',        '\303', },
  522.     { VK_UP,    TRUE,    'H',    '\304',    '\305',        '\306', },
  523.     { VK_PRIOR,    TRUE,    'I',    '\307',    '\204',        '\310', }, /*PgUp*/
  524.     { VK_LEFT,    TRUE,    'K',    '\311',    's',        '\312', },
  525.     { VK_RIGHT,    TRUE,    'M',    '\313',    't',        '\314', },
  526.     { VK_END,    TRUE,    'O',    '\315',    'u',        '\316', },
  527.     { VK_DOWN,    TRUE,    'P',    '\317',    '\320',        '\321', },
  528.     { VK_NEXT,    TRUE,    'Q',    '\322',    'v',        '\323', }, /*PgDn*/
  529.     { VK_INSERT,TRUE,    'R',    '\324',    '\325',        '\326', },
  530.     { VK_DELETE,TRUE,    'S',    '\327',    '\330',        '\331', },
  531.  
  532.     { VK_SNAPSHOT,TRUE,    0,        0,        0,            'r', }, /*PrtScrn*/
  533.  
  534.     /* Most people don't have F13-F20, but what the hell... */
  535.     { VK_F13,    TRUE,    '\332',    '\333',    '\334',        '\335', },
  536.     { VK_F14,    TRUE,    '\336',    '\337',    '\340',        '\341', },
  537.     { VK_F15,    TRUE,    '\342',    '\343',    '\344',        '\345', },
  538.     { VK_F16,    TRUE,    '\346',    '\347',    '\350',        '\351', },
  539.     { VK_F17,    TRUE,    '\352',    '\353',    '\354',        '\355', },
  540.     { VK_F18,    TRUE,    '\356',    '\357',    '\360',        '\361', },
  541.     { VK_F19,    TRUE,    '\362',    '\363',    '\364',        '\365', },
  542.     { VK_F20,    TRUE,    '\366',    '\367',    '\370',        '\371', },
  543. };
  544.  
  545.  
  546. static BOOL g_fJustGotFocus = FALSE;
  547.  
  548. /* The return code indicates key code size. */
  549.     static int
  550. win32_kbd_patch_key(
  551.     KEY_EVENT_RECORD* pker)
  552. {
  553.     static int s_iIsDead = 0;
  554.     static WORD awAnsiCode[2];
  555.     UINT uMods = pker->dwControlKeyState;
  556.     BYTE abKeystate[256];
  557.  
  558.     if (s_iIsDead == 2)
  559.     {
  560.         pker->uChar.AsciiChar = (CHAR) awAnsiCode[1];
  561.         s_iIsDead = 0;
  562.         return 1;
  563.     }
  564.  
  565.     if (pker->uChar.AsciiChar != 0) 
  566.         return 1;
  567.     
  568.     memset(abKeystate, 0, sizeof (abKeystate));
  569.  
  570.     /* Clear any pending dead keys */
  571.     ToAscii(VK_SPACE, MapVirtualKey(VK_SPACE, 0), abKeystate, awAnsiCode, 0);
  572.     
  573.     if (uMods & SHIFT_PRESSED) 
  574.         abKeystate[VK_SHIFT] = 0x80;
  575.     if (uMods & CAPSLOCK_ON) 
  576.         abKeystate[VK_CAPITAL] = 1;
  577.  
  578.     if ((uMods & ALT_GR) == ALT_GR)
  579.     {
  580.         abKeystate[VK_CONTROL] = abKeystate[VK_LCONTROL] =
  581.             abKeystate[VK_MENU] = abKeystate[VK_RMENU] = 0x80;
  582.     }
  583.  
  584.     s_iIsDead = ToAscii(pker->wVirtualKeyCode, pker->wVirtualScanCode,
  585.                         abKeystate, awAnsiCode, 0);
  586.  
  587.     if (s_iIsDead > 0)
  588.         pker->uChar.AsciiChar = (CHAR) awAnsiCode[0];
  589.  
  590.     return s_iIsDead;
  591. }
  592.  
  593.  
  594.   
  595. /*
  596.  * Decode a KEY_EVENT into one or two keystrokes
  597.  */
  598.     static BOOL
  599. decode_key_event(
  600.     KEY_EVENT_RECORD* pker,
  601.     char_u* pch,
  602.     char_u* pchPending,
  603.     BOOL fDoPost)
  604. {
  605.     int i;
  606.     const int nModifs = pker->dwControlKeyState & (SHIFT | ALT | CTRL);
  607.  
  608. #ifdef WIN95_DEAD_KEYS_HACK
  609.     static UINT uDeadVirtKey = 0, uDeadScan = 0, uDeadCtrlState = 0;
  610.     char sz[200];
  611.  
  612.     sprintf(sz, "Vk = 0x%02x, `%c'; Scan = 0x%02x; "
  613.             "Char = %3d, 0x%02x, `%c'; Ctrl = 0x%02x, Post = %d\n",
  614.             pker->wVirtualKeyCode,
  615.             isprint(pker->wVirtualKeyCode) ? pker->wVirtualKeyCode :'?',
  616.             pker->wVirtualScanCode,
  617.             LOBYTE(pker->uChar.AsciiChar), LOBYTE(pker->uChar.AsciiChar),
  618.             isprint(pker->uChar.AsciiChar) ? pker->uChar.AsciiChar :'?',
  619.             pker->dwControlKeyState, fDoPost);
  620.     OutputDebugString(sz);
  621. #endif /* WIN95_DEAD_KEYS_HACK */
  622.  
  623.     *pch = *pchPending = NUL;
  624.     g_fJustGotFocus = FALSE;
  625.     
  626.     /* ignore key up events */
  627.     if (!pker->bKeyDown)
  628.         return FALSE;
  629.  
  630.     /* ignore some keystrokes */
  631.     switch (pker->wVirtualKeyCode)
  632.     {
  633.     /* modifiers */
  634.     case VK_SHIFT:
  635.     case VK_CONTROL:
  636.     case VK_MENU:    /* Alt key */
  637.         return FALSE;
  638.  
  639.     default:
  640.         break;
  641.     }
  642.  
  643. #ifdef WIN95_DEAD_KEYS_HACK
  644.     if (fDoPost  &&  uDeadVirtKey != 0)
  645.     {
  646.         sprintf(sz, "sending vk = 0x%2x, sc = 0x%2x\n", 
  647.                 pker->wVirtualKeyCode, pker->wVirtualScanCode);
  648.         OutputDebugString(sz);
  649.  
  650.         PostMessage(g_hwndKbd, WM_SENDDEADKEY,
  651.                     MAKELONG(MAKEWORD(uDeadVirtKey, uDeadScan), uDeadCtrlState),
  652.                     MAKELONG(MAKEWORD(pker->wVirtualKeyCode,
  653.                                       pker->wVirtualScanCode),
  654.                              pker->dwControlKeyState));
  655.     }
  656. #endif /* WIN95_DEAD_KEYS_HACK */
  657.  
  658. #if 0
  659.     /* If AltGr has been pressed, remove it.  */
  660.     if ((pker->dwControlKeyState & ALT_GR) == ALT_GR)
  661.         pker->dwControlKeyState &= ~ ALT_GR;
  662. #endif
  663.  
  664. #if 0
  665.     /* If CapsLock is on on Win95, digits will be shifted by default;
  666.      * e.g., pressing '1' will give '!'.  Feh. */
  667.     if ((pker->dwControlKeyState & CAPSLOCK_ON) &&
  668.         !(pker->dwControlKeyState & SHIFT_PRESSED) &&
  669.         ('0' <= pker->wVirtualKeyCode  &&  pker->wVirtualKeyCode <= '9'))
  670.     {
  671.         pker->uChar.AsciiChar = NUL;
  672.     }
  673. #endif
  674.  
  675. #if 0
  676.     if ((*pch = pker->uChar.AsciiChar) != NUL)
  677.         return TRUE;
  678. #endif
  679.  
  680.     /* special cases */
  681.     if ((nModifs & CTRL) != 0  &&  (nModifs & ~CTRL) == 0
  682.         &&  pker->uChar.AsciiChar == NUL)
  683.     {
  684.         /* Ctrl-6 is Ctrl-^ */
  685.         if (pker->wVirtualKeyCode == '6')
  686.         {
  687.             *pch = Ctrl('^');
  688.             return TRUE;
  689.         }
  690.         /* Ctrl-2 is Ctrl-@ */
  691.         else if (pker->wVirtualKeyCode == '2')
  692.         {
  693.             *pch = NUL;
  694.             return TRUE;
  695.         }
  696.     }
  697.     
  698.     /* Shift-TAB */
  699.     if (pker->wVirtualKeyCode == VK_TAB  &&  (nModifs & SHIFT_PRESSED))
  700.     {
  701.         *pch = K_NUL;
  702.         *pchPending = '\017';
  703.         return TRUE;
  704.     }
  705.     
  706.     for (i = sizeof(VirtKeyMap) / sizeof(VirtKeyMap[0]);  --i >= 0;  )
  707.     {
  708.         if (VirtKeyMap[i].wVirtKey == pker->wVirtualKeyCode)
  709.         {
  710.             if (nModifs == 0)
  711.                 *pch = VirtKeyMap[i].chAlone;
  712.             else if ((nModifs & SHIFT) != 0 && (nModifs & ~SHIFT) == 0)
  713.                 *pch = VirtKeyMap[i].chShift;
  714.             else if ((nModifs & CTRL) != 0  &&  (nModifs & ~CTRL) == 0)
  715.                 *pch = VirtKeyMap[i].chCtrl;
  716.             else if ((nModifs & ALT) != 0  &&  (nModifs & ~ALT) == 0)
  717.                 *pch = VirtKeyMap[i].chAlt;
  718.  
  719.             if (*pch != 0)
  720.             {
  721.                 if (VirtKeyMap[i].fAnsiKey)
  722.                 {
  723.                     *pchPending = *pch;
  724.                     *pch = K_NUL;
  725.                 }
  726.  
  727.                 return TRUE;
  728.             }
  729.         }
  730.     }
  731.  
  732.     i = win32_kbd_patch_key(pker);
  733.  
  734.     if (i < 0)
  735.     {
  736.         *pch = NUL;
  737.  
  738. #ifdef WIN95_DEAD_KEYS_HACK
  739.         if (fDoPost)
  740.         {
  741.             uDeadVirtKey = pker->wVirtualKeyCode;
  742.             uDeadScan = pker->wVirtualScanCode;
  743.             uDeadCtrlState = pker->dwControlKeyState;
  744.         }
  745. #endif /* WIN95_DEAD_KEYS_HACK */
  746.     }
  747.     else
  748.     {
  749. #ifdef WIN95_DEAD_KEYS_HACK
  750.         uDeadVirtKey = uDeadScan = uDeadCtrlState = 0;
  751. #endif /* WIN95_DEAD_KEYS_HACK */
  752.         *pch = (i > 0)  ?  pker->uChar.AsciiChar  :  NUL;
  753.     }
  754.  
  755.     return (*pch != NUL);
  756. }
  757.  
  758.  
  759. #ifdef USE_MOUSE
  760.  
  761. static int g_fMouseAvail = FALSE;    /* mouse present */
  762. static int g_fMouseActive = FALSE;    /* mouse enabled */
  763. static int g_nMouseClick = -1;        /* mouse status */
  764. static int g_xMouse;                /* mouse x coordinate */
  765. static int g_yMouse;                /* mouse y coordinate */
  766.  
  767. /*
  768.  * Enable or disable mouse input
  769.  */
  770.     void
  771. mch_setmouse(
  772.     int on)
  773. {
  774.     DWORD cmodein;
  775.  
  776.     if (! g_fMouseAvail)
  777.         return;
  778.     
  779.     g_fMouseActive = on;
  780.     GetConsoleMode(g_hConIn, &cmodein);
  781.  
  782.     if (g_fMouseActive)
  783.         cmodein |= ENABLE_MOUSE_INPUT;
  784.     else
  785.         cmodein &= ~ENABLE_MOUSE_INPUT;
  786.  
  787.     SetConsoleMode(g_hConIn, cmodein);
  788. }
  789.  
  790.  
  791. /*
  792.  * Decode a MOUSE_EVENT.  If it's a valid event, return MOUSE_LEFT,
  793.  * MOUSE_MIDDLE, or MOUSE_RIGHT for a click; MOUSE_DRAG for a mouse
  794.  * move with a button held down; and MOUSE_RELEASE after a MOUSE_DRAG
  795.  * or a MOUSE_LEFT, _MIDDLE, or _RIGHT.  We encode the button type,
  796.  * the number of clicks, and the Shift/Ctrl/Alt modifiers in g_nMouseClick,
  797.  * and we return the mouse position in g_xMouse and g_yMouse.
  798.  *
  799.  * Every MOUSE_LEFT, _MIDDLE, or _RIGHT will be followed by zero or more
  800.  * MOUSE_DRAGs and one MOUSE_RELEASE.  MOUSE_RELEASE will be followed only
  801.  * by MOUSE_LEFT, _MIDDLE, or _RIGHT.
  802.  *
  803.  * For multiple clicks, we send, say, MOUSE_LEFT/1 click, MOUSE_RELEASE,
  804.  * MOUSE_LEFT/2 clicks, MOUSE_RELEASE, MOUSE_LEFT/3 clicks, MOUSE_RELEASE, ....
  805.  *
  806.  * Windows will send us MOUSE_MOVED notifications whenever the mouse
  807.  * moves, even if it stays within the same character cell.  We ignore
  808.  * all MOUSE_MOVED messages if the position hasn't really changed, and
  809.  * we ignore all MOUSE_MOVED messages where no button is held down (i.e.,
  810.  * we're only interested in MOUSE_DRAG).
  811.  *
  812.  * All of this is complicated by the code that fakes MOUSE_MIDDLE on
  813.  * 2-button mouses by pressing the left & right buttons simultaneously.
  814.  * In practice, it's almost impossible to click both at the same time,
  815.  * so we need to delay a little.  Also, we tend not to get MOUSE_RELEASE
  816.  * in such cases, if the user is clicking quickly.
  817.  */
  818.     static BOOL
  819. decode_mouse_event(
  820.     MOUSE_EVENT_RECORD* pmer)
  821. {
  822.     static int s_nOldButton = -1;
  823.     static int s_nOldMouseClick = -1;
  824.     static int s_xOldMouse = -1;
  825.     static int s_yOldMouse = -1;
  826.     static linenr_t s_old_topline = 0;
  827.     static int s_cClicks = 1;
  828.     static BOOL s_fReleased = TRUE;
  829.     static s_dwLastClickTime = 0;
  830.     static BOOL s_fNextIsMiddle = FALSE;
  831.  
  832.     const DWORD LEFT = FROM_LEFT_1ST_BUTTON_PRESSED;
  833.     const DWORD MIDDLE = FROM_LEFT_2ND_BUTTON_PRESSED;
  834.     const DWORD RIGHT = RIGHTMOST_BUTTON_PRESSED;
  835.     const DWORD LEFT_RIGHT = LEFT | RIGHT;
  836.  
  837.     int nButton;
  838.  
  839.     if (! g_fMouseAvail  ||  ! g_fMouseActive)
  840.     {
  841.         g_nMouseClick = -1;
  842.         return FALSE;
  843.     }
  844.  
  845.     /* get a spurious MOUSE_EVENT immediately after receiving focus; ignore */
  846.     if (g_fJustGotFocus)
  847.     {
  848.         g_fJustGotFocus = FALSE;
  849.         return FALSE;
  850.     }
  851.     
  852.     /* unprocessed mouse click? */
  853.     if (g_nMouseClick != -1)
  854.         return TRUE;
  855.  
  856.     nButton = -1;
  857.     g_xMouse = pmer->dwMousePosition.X;
  858.     g_yMouse = pmer->dwMousePosition.Y;
  859.  
  860.     if (pmer->dwEventFlags == MOUSE_MOVED)
  861.     {
  862.         /* ignore MOUSE_MOVED events if (x, y) hasn't changed.  (We get these
  863.          * events even when the mouse moves only within a char cell.) */
  864.         if (s_xOldMouse == g_xMouse  &&  s_yOldMouse == g_yMouse)
  865.             return FALSE;
  866.     }
  867.  
  868.     /* If no buttons are pressed... */
  869.     if (pmer->dwButtonState == 0)
  870.     {
  871.         /* If the last thing returned was MOUSE_RELEASE, ignore this */
  872.         if (s_fReleased)
  873.             return FALSE;
  874.  
  875.         nButton = MOUSE_RELEASE;
  876.         s_fReleased = TRUE;
  877.     }
  878.     else    /* one or more buttons pressed */
  879.     {
  880.         const int cButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
  881.  
  882.         /* on a 2-button mouse, hold down left and right buttons
  883.          * simultaneously to get MIDDLE. */
  884.  
  885.         if (cButtons == 2  &&  s_nOldButton != MOUSE_DRAG)
  886.         {
  887.             DWORD dwLR = (pmer->dwButtonState & LEFT_RIGHT);
  888.             
  889.             /* if either left or right button only is pressed, see if the
  890.              * the next mouse event has both of them pressed */
  891.             if (dwLR == LEFT  ||  dwLR  == RIGHT)
  892.             {
  893.                 for (;;)
  894.                 {
  895.                     /* wait a short time for next input event */
  896.                     if (WaitForSingleObject(g_hConIn, p_mouset / 3)
  897.                         != WAIT_OBJECT_0)
  898.                         break;
  899.                     else
  900.                     {
  901.                         DWORD cRecords = 0;
  902.                         INPUT_RECORD ir;
  903.                         MOUSE_EVENT_RECORD* pmer2 = &ir.Event.MouseEvent;
  904.                         
  905.                         PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
  906.                     
  907.                         if (cRecords == 0  ||  ir.EventType != MOUSE_EVENT
  908.                             ||  !(pmer2->dwButtonState & LEFT_RIGHT))
  909.                             break;
  910.                         else
  911.                         {
  912.                             if (pmer2->dwEventFlags != MOUSE_MOVED)
  913.                             {
  914.                                 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  915.                                 
  916.                                 return decode_mouse_event(pmer2);
  917.                             }
  918.                             else if (s_xOldMouse == pmer2->dwMousePosition.X &&
  919.                                      s_yOldMouse == pmer2->dwMousePosition.Y)
  920.                             {
  921.                                 /* throw away spurious mouse move */
  922.                                 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  923.  
  924.                                 /* are there any more mouse events in queue? */
  925.                                 PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
  926.  
  927.                                 if (cRecords==0 || ir.EventType != MOUSE_EVENT)
  928.                                     break;
  929.                             }
  930.                             else
  931.                                 break;
  932.                         }
  933.                     }
  934.                 }
  935.             }
  936.         }
  937.  
  938.         if (s_fNextIsMiddle)
  939.         {
  940.             nButton = (pmer->dwEventFlags == MOUSE_MOVED)
  941.                 ? MOUSE_DRAG : MOUSE_MIDDLE;
  942.             s_fNextIsMiddle = FALSE;
  943.         }
  944.         else if (cButtons == 2  &&
  945.             ((pmer->dwButtonState & LEFT_RIGHT) == LEFT_RIGHT))
  946.         {
  947.             nButton = MOUSE_MIDDLE;
  948.  
  949.             if (! s_fReleased  &&  pmer->dwEventFlags != MOUSE_MOVED)
  950.             {
  951.                 s_fNextIsMiddle = TRUE;
  952.                 nButton = MOUSE_RELEASE;
  953.             }
  954.         }
  955.         else if ((pmer->dwButtonState & LEFT) == LEFT)
  956.             nButton = MOUSE_LEFT;
  957.         else if ((pmer->dwButtonState & MIDDLE) == MIDDLE)
  958.             nButton = MOUSE_MIDDLE;
  959.         else if ((pmer->dwButtonState & RIGHT) == RIGHT)
  960.             nButton = MOUSE_RIGHT;
  961.  
  962.         if (! s_fReleased  && ! s_fNextIsMiddle
  963.             &&  nButton != s_nOldButton  &&  s_nOldButton != MOUSE_DRAG)
  964.             return FALSE;
  965.         
  966.         s_fReleased = s_fNextIsMiddle;
  967.     }
  968.  
  969.     if (pmer->dwEventFlags == 0  ||  pmer->dwEventFlags == DOUBLE_CLICK)
  970.     {
  971.         /* button pressed or released, without mouse moving */
  972.         if (nButton != -1  &&  nButton != MOUSE_RELEASE)
  973.         {
  974.             DWORD dwCurrentTime = GetTickCount();
  975.  
  976.             if (s_xOldMouse != g_xMouse  ||  s_yOldMouse != g_yMouse
  977.                 ||  s_nOldButton != nButton
  978.                 ||  s_old_topline != curwin->w_topline
  979.                 ||  (int) (dwCurrentTime - s_dwLastClickTime) > p_mouset)
  980.             {
  981.                 s_cClicks = 1;
  982.             }
  983.             else if (++s_cClicks > 4)
  984.             {
  985.                 s_cClicks = 1;
  986.             }
  987.  
  988.             s_dwLastClickTime = dwCurrentTime;
  989.         }
  990.     }
  991.     else if (pmer->dwEventFlags == MOUSE_MOVED)
  992.     {
  993.         if (nButton != -1  &&  nButton != MOUSE_RELEASE)
  994.             nButton = MOUSE_DRAG;
  995.  
  996.         s_cClicks = 1;
  997.     }
  998.  
  999.     if (nButton == -1)
  1000.         return FALSE;
  1001.     
  1002.     if (nButton != MOUSE_RELEASE)
  1003.         s_nOldButton = nButton;
  1004.     
  1005.     g_nMouseClick = nButton;
  1006.  
  1007.     if (pmer->dwControlKeyState & SHIFT_PRESSED)
  1008.         g_nMouseClick |= MOUSE_SHIFT;
  1009.     if (pmer->dwControlKeyState & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
  1010.         g_nMouseClick |= MOUSE_CTRL;
  1011.     if (pmer->dwControlKeyState & (RIGHT_ALT_PRESSED  | LEFT_ALT_PRESSED))
  1012.         g_nMouseClick |= MOUSE_ALT;
  1013.  
  1014.     /* only pass on interesting (i.e., different) mouse events */
  1015.     if (s_xOldMouse == g_xMouse  &&  s_yOldMouse == g_yMouse
  1016.         &&  s_nOldMouseClick == g_nMouseClick)
  1017.     {
  1018.         g_nMouseClick = -1;
  1019.         return FALSE;
  1020.     }
  1021.  
  1022.     g_nMouseClick |= 0x20;
  1023.  
  1024.     s_xOldMouse = g_xMouse;
  1025.     s_yOldMouse = g_yMouse;
  1026.     s_old_topline = curwin->w_topline;
  1027.     s_nOldMouseClick = g_nMouseClick;
  1028.     
  1029.     if (nButton != MOUSE_DRAG  &&  nButton != MOUSE_RELEASE)
  1030.         SET_NUM_MOUSE_CLICKS(g_nMouseClick, s_cClicks);
  1031.     
  1032.     return TRUE;
  1033. }
  1034.  
  1035.  
  1036. #endif /* USE_MOUSE */
  1037.  
  1038.  
  1039. /*
  1040.  * Wait until console input from keyboard or mouse is available,
  1041.  * or the time is up
  1042.  */
  1043.     static int
  1044. WaitForChar(
  1045.     long msec)
  1046. {
  1047.     DWORD dwNow, dwEndTime;
  1048.  
  1049.     if (msec <= 0)
  1050.         msec = 2;    /* very short time */
  1051.  
  1052.     dwEndTime = GetTickCount() + msec;
  1053.  
  1054.     /* We need to loop until the end of the time period, because
  1055.      * we might get multiple unusable mouse events in that time.
  1056.      */
  1057.     while ((dwNow = GetTickCount())  <  dwEndTime)
  1058.     {
  1059.         if (WaitForSingleObject(g_hConIn, dwEndTime - dwNow) == WAIT_OBJECT_0)
  1060.         {
  1061.             INPUT_RECORD ir;
  1062.             DWORD cRecords = 0;
  1063.             
  1064.             PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1065.             
  1066.             if (cRecords > 0)
  1067.             {
  1068.                 if (ir.EventType == KEY_EVENT  &&  ir.Event.KeyEvent.bKeyDown)
  1069.                 {
  1070.                     char_u ch, ch2;
  1071.  
  1072.                     return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2,
  1073.                                             FALSE);
  1074.                 }
  1075.                 else if (ir.EventType == FOCUS_EVENT)
  1076.                 {
  1077.                     ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1078.                     
  1079.                     g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
  1080.                 }
  1081.                 else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
  1082.                 {
  1083.                     ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1084.                     
  1085.                     set_winsize(Rows, Columns, FALSE);
  1086.                 }
  1087. #ifdef USE_MOUSE
  1088.                 else if (ir.EventType == MOUSE_EVENT) 
  1089.                 {
  1090.                     ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1091.                     
  1092.                     if (decode_mouse_event(&ir.Event.MouseEvent))
  1093.                         return TRUE;
  1094.                 }
  1095. #endif /* USE_MOUSE */
  1096.                 else
  1097.                 {
  1098.                     /* Discard it, it's an insignificant event */
  1099.                     ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1100.                 }
  1101.             }
  1102.         }
  1103.     }
  1104.         
  1105.     return FALSE;
  1106. }
  1107.  
  1108.  
  1109. static char_u g_chPending = NUL;
  1110.  
  1111.  
  1112. /*
  1113.  * Is there a pending keystroke or mouse event?
  1114.  */
  1115.     static BOOL
  1116. kbhit()
  1117. {
  1118.     if (g_chPending != NUL
  1119. #ifdef USE_MOUSE
  1120.         || g_nMouseClick != -1
  1121. #endif /* USE_MOUSE */
  1122.         )
  1123.         return TRUE;
  1124.  
  1125.     for ( ; ; )
  1126.     {
  1127.         INPUT_RECORD ir;
  1128.         DWORD cRecords = 0;
  1129.  
  1130.         PeekConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1131.  
  1132.         if (cRecords == 0)
  1133.             return FALSE;
  1134.         else
  1135.         {
  1136.             if (ir.EventType == KEY_EVENT  &&  ir.Event.KeyEvent.bKeyDown)
  1137.             {
  1138.                 char_u ch, ch2;
  1139.  
  1140.                 return decode_key_event(&ir.Event.KeyEvent, &ch, &ch2, FALSE);
  1141.             }
  1142.             else if (ir.EventType == FOCUS_EVENT)
  1143.             {
  1144.                 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1145.                 
  1146.                 g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
  1147.             }
  1148.             else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
  1149.             {
  1150.                 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1151.                 
  1152.                 set_winsize(Rows, Columns, FALSE);
  1153.             }
  1154. #ifdef USE_MOUSE
  1155.             else if (ir.EventType == MOUSE_EVENT) 
  1156.             {
  1157.                 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1158.  
  1159.                 if (decode_mouse_event(&ir.Event.MouseEvent))
  1160.                     return TRUE;
  1161.             }
  1162. #endif /* USE_MOUSE */
  1163.             else
  1164.             {
  1165.                 /* Discard it, it's an insignificant event */
  1166.                 ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1167.             }
  1168.         }
  1169.     }
  1170. }
  1171.  
  1172.  
  1173. /*
  1174.  * Get a keystroke or a mouse event
  1175.  */
  1176.     static char_u
  1177. tgetch()
  1178. {
  1179.     char_u ch;
  1180.  
  1181.     if (g_chPending != NUL)
  1182.     {
  1183.         ch = g_chPending;
  1184.         g_chPending = NUL;
  1185.         return ch;
  1186.     }
  1187.  
  1188.     for ( ; ; )
  1189.     {
  1190.         INPUT_RECORD ir;
  1191.         DWORD cRecords = 0;
  1192.         
  1193.         ReadConsoleInput(g_hConIn, &ir, 1, &cRecords);
  1194.         
  1195.         if (ir.EventType == KEY_EVENT)
  1196.         {
  1197.             if (decode_key_event(&ir.Event.KeyEvent, &ch, &g_chPending, TRUE))
  1198.                 return ch;
  1199.         }
  1200.         else if (ir.EventType == FOCUS_EVENT)
  1201.         {
  1202.             g_fJustGotFocus = ir.Event.FocusEvent.bSetFocus;
  1203.         }
  1204.         else if (ir.EventType == WINDOW_BUFFER_SIZE_EVENT)
  1205.         {
  1206.             set_winsize(Rows, Columns, FALSE);
  1207.         } 
  1208. #ifdef USE_MOUSE
  1209.         else if (ir.EventType == MOUSE_EVENT) 
  1210.         {
  1211.             if (decode_mouse_event(&ir.Event.MouseEvent))
  1212.                 return 0;
  1213.         }
  1214. #endif /* USE_MOUSE */
  1215.     }
  1216. }
  1217.  
  1218.  
  1219. /*
  1220.  * mch_inchar(): low-level input funcion.
  1221.  * Get one or more characters from the keyboard or the mouse.
  1222.  * If time == 0, do not wait for characters.
  1223.  * If time == n, wait a short time for characters.
  1224.  * If time == -1, wait forever for characters.
  1225.  * Returns the number of characters read into buf.
  1226.  */
  1227.     int
  1228. mch_inchar(
  1229.     char_u *buf,
  1230.     int maxlen,
  1231.     long time)
  1232. {
  1233.     int len = 0;
  1234.     int c;
  1235.  
  1236.     if (time >= 0)
  1237.     {
  1238.         if (! WaitForChar(time))     /* no character available */
  1239.             return 0;
  1240.     }
  1241.     else    /* time == -1, wait forever */
  1242.     {
  1243.     
  1244.         /* If there is no character available within 2 seconds (default),
  1245.          * write the autoscript file to disk */
  1246.         if (WaitForChar(p_ut) == 0)
  1247.             updatescript(0);
  1248.     }
  1249.  
  1250.     /*
  1251.      * Try to read as many characters as there are.
  1252.      */
  1253.  
  1254.     --maxlen;                   /* may get two chars at once */
  1255.  
  1256.     /* we will get at least one key. Get more if they are available. */
  1257.     g_fCBrkPressed = FALSE;
  1258.  
  1259. #ifdef MCH_WRITE_DUMP
  1260.     if (fdDump)
  1261.         fputc('[', fdDump);
  1262. #endif /* MCH_WRITE_DUMP */
  1263.  
  1264.     while ((len == 0 || kbhit())  &&  len < maxlen)
  1265.     {
  1266. #ifdef USE_MOUSE
  1267.         if (g_nMouseClick != -1  &&  maxlen >= 5-1)
  1268.         {
  1269. # ifdef MCH_WRITE_DUMP
  1270.             if (fdDump)
  1271.                 fprintf(fdDump, "{%02x @ %d, %d}",
  1272.                         g_nMouseClick, g_xMouse, g_yMouse);
  1273. # endif /* MCH_WRITE_DUMP */
  1274.  
  1275.             len = 5;
  1276.             *buf++ = ESC + 128;
  1277.             *buf++ = 'M';
  1278.             *buf++ = g_nMouseClick;
  1279.             *buf++ = g_xMouse + '!';
  1280.             *buf++ = g_yMouse + '!';
  1281.             g_nMouseClick = -1;
  1282.             
  1283.         }
  1284.         else
  1285. #endif /* USE_MOUSE */
  1286.         {
  1287.             if ((c = tgetch()) == Ctrl('C'))
  1288.                 g_fCBrkPressed = TRUE;
  1289.             
  1290. #ifdef USE_MOUSE
  1291.             if (g_nMouseClick == -1)
  1292. #endif /* USE_MOUSE */
  1293.             {
  1294.                 *buf++ = c;
  1295.                 len++;
  1296.                 
  1297. #ifdef MCH_WRITE_DUMP
  1298.                 if (fdDump)
  1299.                     fputc(c, fdDump);
  1300. #endif /* MCH_WRITE_DUMP */
  1301.                 
  1302.             }
  1303.         }
  1304.     }
  1305.  
  1306. #ifdef MCH_WRITE_DUMP
  1307.     if (fdDump)
  1308.     {
  1309.         fputs("]\n", fdDump);
  1310.         fflush(fdDump);
  1311.     }
  1312. #endif /* MCH_WRITE_DUMP */
  1313.  
  1314.     beep_count = 0;            /* may beep again now that we got some chars */
  1315.     return len;
  1316. }
  1317.  
  1318.  
  1319. static char g_szOrigTitle[256];
  1320. static int  g_fWindInitCalled = FALSE;
  1321. static CONSOLE_CURSOR_INFO g_cci;
  1322. static DWORD g_cmodein = 0;
  1323. static DWORD g_cmodeout = 0;
  1324.  
  1325. /*
  1326.  * Because of a bug in the Windows 95 Console, we need to set the screen size
  1327.  * back when switching screens.
  1328.  */
  1329. static int g_nOldRows = 0;
  1330. static int g_nOldColumns = 0;
  1331.  
  1332. /*
  1333.  * platform-specific initialization
  1334.  */
  1335.     void
  1336. mch_windinit()
  1337. {
  1338.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  1339.     extern int _fmode;
  1340.  
  1341.     PlatformId();
  1342.  
  1343.     _fmode = O_BINARY;          /* we do our own CR-LF translation */
  1344.     flushbuf();
  1345.  
  1346.     /* Obtain handles for the standard Console I/O devices */
  1347.     g_hConIn =  GetStdHandle(STD_INPUT_HANDLE);
  1348.     g_hSavOut = GetStdHandle(STD_OUTPUT_HANDLE);
  1349.     g_hCurOut = g_hSavOut;
  1350.  
  1351.     /* Get current text attributes */
  1352.     GetConsoleScreenBufferInfo(g_hSavOut, &csbi);
  1353.     g_attrCurrent = g_attrDefault = csbi.wAttributes;
  1354.     GetConsoleCursorInfo(g_hSavOut, &g_cci);
  1355.  
  1356.     GetConsoleMode(g_hConIn,  &g_cmodein);
  1357.     GetConsoleMode(g_hSavOut, &g_cmodeout);
  1358.  
  1359.     GetConsoleTitle(g_szOrigTitle, sizeof(g_szOrigTitle));
  1360.     mch_get_winsize();
  1361.  
  1362.     g_nOldRows = Rows;
  1363.     g_nOldColumns = Columns;
  1364.  
  1365. #ifdef MCH_WRITE_DUMP
  1366.     fdDump = fopen("dump", "wt");
  1367.  
  1368.     if (fdDump)
  1369.     {
  1370.         time_t t;
  1371.  
  1372.         time(&t);
  1373.         fputs(ctime(&t), fdDump);
  1374.         fflush(fdDump);
  1375.     }
  1376. #endif /* MCH_WRITE_DUMP */
  1377.  
  1378.     g_fWindInitCalled = TRUE;
  1379.  
  1380. #ifdef USE_MOUSE
  1381.     g_fMouseAvail = GetSystemMetrics(SM_MOUSEPRESENT);
  1382. #endif
  1383.  
  1384. #ifdef WIN95_DEAD_KEYS_HACK
  1385.     keyboard_init();
  1386. #endif
  1387.  
  1388.     /*
  1389.      * We don't really want to jump to our own screen yet; do that after
  1390.      * starttermcap().  This flashes the window, sorry about that, but
  1391.      * otherwise "vim -r" doesn't work.
  1392.      */
  1393.     g_hCurOut = g_hSavOut;
  1394.     SetConsoleActiveScreenBuffer(g_hCurOut);
  1395. }
  1396.  
  1397.  
  1398. /*
  1399.  * Shut down and exit with status `r'
  1400.  * Careful: mch_windexit() may be called before mch_windinit()!
  1401.  */
  1402.     void
  1403. mch_windexit(
  1404.     int r)
  1405. {
  1406.     stoptermcap();
  1407.     flushbuf();
  1408.  
  1409.     if (g_fWindInitCalled)
  1410.         settmode(0);
  1411.  
  1412.     if (g_hConOut != INVALID_HANDLE_VALUE)
  1413.     {
  1414.         (void) CloseHandle(g_hConOut);
  1415.  
  1416.         if (g_hSavOut != INVALID_HANDLE_VALUE)
  1417.         {
  1418.             SetConsoleTextAttribute(g_hSavOut, g_attrDefault);
  1419.             SetConsoleCursorInfo(g_hSavOut, &g_cci);
  1420.         }
  1421.     }
  1422.  
  1423.     ml_close_all(TRUE);             /* remove all memfiles */
  1424.  
  1425.     if (g_fWindInitCalled)
  1426.     {
  1427. #ifdef WIN95_DEAD_KEYS_HACK
  1428.         keyboard_exit();
  1429. #endif
  1430.  
  1431.         mch_restore_title(3);
  1432.  
  1433. #ifdef MCH_WRITE_DUMP
  1434.         if (fdDump)
  1435.         {
  1436.             time_t t;
  1437.             
  1438.             time(&t);
  1439.             fputs(ctime(&t), fdDump);
  1440.             fclose(fdDump);
  1441.         }
  1442.         fdDump = NULL;
  1443. #endif /* MCH_WRITE_DUMP */
  1444.     }
  1445.  
  1446.     exit(r);
  1447. }
  1448.  
  1449.  
  1450. /*
  1451.  * Do we have an interactive window?
  1452.  */
  1453.     int
  1454. mch_check_win(
  1455.     int argc,
  1456.     char **argv)
  1457. {
  1458.     if (isatty(1))
  1459.         return OK;
  1460.     return FAIL;
  1461. }
  1462.  
  1463.  
  1464. /*
  1465.  * Is input interactive?  (From a keyboard)
  1466.  */
  1467.     int
  1468. mch_check_input()
  1469. {
  1470.     if (isatty(0))
  1471.         return OK;
  1472.     return FAIL;
  1473. }
  1474.  
  1475.  
  1476. /*
  1477.  * Turn a filename into its canonical form.  Replace slashes with backslashes.
  1478.  * This used to replace backslashes with slashes, but that caused problems
  1479.  * when using the file name as a command.  We can't have a mix of slashes and
  1480.  * backslashes, because comparing file names will not work correctly.  The
  1481.  * commands that use file names should be prepared to handle the backslashes.
  1482.  */
  1483.     static void
  1484. canonicalize_filename(
  1485.     char* pszName)
  1486. {
  1487.     if (pszName == NULL)
  1488.         return;
  1489.     
  1490.     for ( ; *pszName;  pszName++)
  1491.     {
  1492.         if (*pszName == '/')
  1493.             *pszName = '\\';
  1494. #ifdef DOWNCASE_FILENAMES
  1495.         else
  1496.             *pszName = tolower(*pszName);
  1497. #endif /* DOWNCASE_FILENAMES */
  1498.     }
  1499. }
  1500.  
  1501.  
  1502. /*
  1503.  * fname_case(): Set the case of the filename, if it already exists.
  1504.  */
  1505.     void
  1506. fname_case(
  1507.     char_u *name)
  1508. {
  1509. #ifdef DOWNCASE_FILENAMES
  1510.     canonicalize_filename(name);
  1511. #else /* !DOWNCASE_FILENAMES */
  1512.     char szTrueName[_MAX_PATH + 1];
  1513.     char *psz, *pszPrev;
  1514.     const int len = (name != NULL)  ?  STRLEN(name)  :  0;
  1515.     
  1516.     if (len == 0)
  1517.         return;
  1518.     
  1519.     STRCPY(szTrueName, name);
  1520.     STRCAT(szTrueName, "\\");    /* sentinel */
  1521.  
  1522.     for (psz = szTrueName;  *psz != NUL;  psz++)
  1523.         if (*psz == '/')
  1524.             *psz = '\\';
  1525.  
  1526.     psz = pszPrev = szTrueName;
  1527.  
  1528.     /* Skip leading \\ in UNC name or drive letter */
  1529.     if (len > 2   &&  ((psz[0] == '\\'  &&  psz[1] == '\\')
  1530.                        || (isalpha(psz[0])  &&  psz[1] == ':')))
  1531.     {
  1532.         psz = pszPrev = szTrueName + 2;
  1533.     }
  1534.     
  1535.     while (*psz != NUL)
  1536.     {
  1537.         WIN32_FIND_DATA fb;
  1538.         HANDLE          hFind;
  1539.  
  1540.         while (*psz != '\\')
  1541.             psz++;
  1542.         *psz = NUL;
  1543.         
  1544.         if ((hFind = FindFirstFile(szTrueName, &fb)) != INVALID_HANDLE_VALUE)
  1545.         {
  1546.             /* avoid ".." and ".", etc */
  1547.             if ((size_t) (psz - pszPrev) == STRLEN(fb.cFileName))
  1548.                 STRCPY(pszPrev, fb.cFileName);
  1549.             FindClose(hFind);
  1550.         }
  1551.         
  1552.         *psz++ = '\\';
  1553.         pszPrev = psz;
  1554.     }
  1555.  
  1556.     *--psz = NUL;    /* remove sentinel */
  1557.  
  1558.     STRCPY(name, szTrueName);
  1559. #endif /* !DOWNCASE_FILENAMES */
  1560. }
  1561.  
  1562.  
  1563. /*
  1564.  * mch_settitle(): set titlebar of our window
  1565.  * Can the icon also be set?
  1566.  */
  1567.     void
  1568. mch_settitle(
  1569.     char_u *title,
  1570.     char_u *icon)
  1571. {
  1572.     if (title != NULL)
  1573.         SetConsoleTitle(title);
  1574. }
  1575.  
  1576.  
  1577. /*
  1578.  * Restore the window/icon title.
  1579.  * which is one of:
  1580.  *    1: Just restore title
  1581.  *  2: Just restore icon (which we don't have)
  1582.  *    3: Restore title and icon (which we don't have)
  1583.  */
  1584.     void
  1585. mch_restore_title(
  1586.     int which)
  1587. {
  1588.     mch_settitle((which & 1) ? g_szOrigTitle : NULL, NULL);
  1589. }
  1590.  
  1591.  
  1592. /*
  1593.  * Return TRUE if we can restore the title (we can)
  1594.  */
  1595.     int
  1596. mch_can_restore_title()
  1597. {
  1598.     return TRUE;
  1599. }
  1600.  
  1601.  
  1602. /*
  1603.  * Return TRUE if we can restore the icon (we can't)
  1604.  */
  1605.     int
  1606. mch_can_restore_icon()
  1607. {
  1608.     return FALSE;
  1609. }
  1610.  
  1611.  
  1612. /*
  1613.  * Insert user name in s[len].
  1614.  */
  1615.     int
  1616. mch_get_user_name(
  1617.     char_u *s,
  1618.     int len)
  1619. {
  1620.     char szUserName[MAX_COMPUTERNAME_LENGTH + 1];
  1621.     DWORD cch = sizeof szUserName;
  1622.  
  1623.     if (GetUserName(szUserName, &cch))
  1624.     {
  1625.         STRNCPY(s, szUserName, len);
  1626.         return OK;
  1627.     }
  1628.     s[0] = NUL;
  1629.     return FAIL;
  1630. }
  1631.  
  1632.  
  1633. /*
  1634.  * Insert host name in s[len].
  1635.  */
  1636.     void
  1637. mch_get_host_name(
  1638.     char_u *s,
  1639.     int len)
  1640. {
  1641.     char szHostName[MAX_COMPUTERNAME_LENGTH + 1];
  1642.     DWORD cch = sizeof szHostName;
  1643.  
  1644.     if (GetComputerName(szHostName, &cch))
  1645.     {
  1646.         STRCPY(s, "PC ");
  1647.         STRNCPY(s + 3, szHostName, len - 3);
  1648.     }
  1649.     else
  1650.         STRNCPY(s, "PC (Win32 Vim)", len);
  1651. }
  1652.  
  1653.  
  1654. /*
  1655.  * return process ID
  1656.  */
  1657.     long
  1658. mch_get_pid()
  1659. {
  1660.     return (long) GetCurrentProcessId();
  1661. }
  1662.  
  1663.  
  1664. /*
  1665.  * Get name of current directory into buffer 'buf' of length 'len' bytes.
  1666.  * Return OK for success, FAIL for failure.
  1667.  */
  1668.     int
  1669. mch_dirname(
  1670.     char_u *buf,
  1671.     int len)
  1672. {
  1673.     return (getcwd(buf, len) != NULL ? OK : FAIL);
  1674. }
  1675.  
  1676.  
  1677. /*
  1678.  * Get absolute filename into buffer 'buf' of length 'len' bytes,
  1679.  * turning all '/'s into '\\'s and getting the correct case of each
  1680.  * component of the filename.  Return OK or FAIL.
  1681.  */
  1682.     int
  1683. FullName(
  1684.     char_u *fname,
  1685.     char_u *buf,
  1686.     int len,
  1687.     int force)
  1688. {
  1689.     int nResult = FAIL;
  1690.     
  1691.     if (fname == NULL)          /* always fail */
  1692.         return FAIL;
  1693.  
  1694.     if (_fullpath(buf, fname, len) == NULL)
  1695.     {
  1696.         /* failed, use the relative path name */
  1697.         STRNCPY(buf, fname, len);
  1698.     }
  1699.     else
  1700.         nResult = OK;
  1701.  
  1702.     fname_case(buf);
  1703.  
  1704.     return nResult;
  1705. }
  1706.  
  1707.  
  1708. /*
  1709.  * return TRUE if `fname' is an absolute path name
  1710.  */
  1711.     int
  1712. isFullName(
  1713.     char_u *fname)
  1714. {
  1715.     char szName[_MAX_PATH];
  1716.  
  1717.     FullName(fname, szName, _MAX_PATH, FALSE);
  1718.  
  1719. #ifdef DOWNCASE_FILENAMES
  1720.     return stricmp(fname, szName) == 0;
  1721. #else /* !DOWNCASE_FILENAMES */
  1722.     return strcmp(fname, szName) == 0;
  1723. #endif /* !DOWNCASE_FILENAMES */
  1724. }
  1725.  
  1726.  
  1727. /*
  1728.  * get file permissions for `name'
  1729.  * -1 : error
  1730.  * else FILE_ATTRIBUTE_* defined in winnt.h
  1731.  */
  1732.     long
  1733. getperm(
  1734.     char_u *name)
  1735. {
  1736.     return GetFileAttributes(name);
  1737. }
  1738.  
  1739.  
  1740. /*
  1741.  * set file permission for `name' to `perm'
  1742.  */
  1743.     int
  1744. setperm(
  1745.     char_u *name,
  1746.     long perm)
  1747. {
  1748.     perm |= FILE_ATTRIBUTE_ARCHIVE;        /* file has changed, set archive bit */
  1749.     return SetFileAttributes(name, perm) ? OK : FAIL;
  1750. }
  1751.  
  1752.  
  1753. /*
  1754.  * return TRUE if "name" is a directory
  1755.  * return FALSE if "name" is not a directory or upon error
  1756.  */
  1757.     int
  1758. mch_isdir(
  1759.     char_u *name)
  1760. {
  1761.     int f = getperm(name);
  1762.  
  1763.     if (f == -1)
  1764.         return FALSE;              /* file does not exist at all */
  1765.  
  1766.     return (f & FILE_ATTRIBUTE_DIRECTORY) != 0;
  1767. }
  1768.  
  1769.  
  1770. /*
  1771.  * handler for ctrl-break, ctrl-c interrupts, and fatal events. 
  1772.  */
  1773.     static BOOL WINAPI
  1774. handler_routine(
  1775.     DWORD dwCtrlType)
  1776. {
  1777.     switch (dwCtrlType)
  1778.     {
  1779.     case CTRL_C_EVENT:
  1780.         g_fCtrlCPressed = TRUE;
  1781.         return TRUE;
  1782.  
  1783.     case CTRL_BREAK_EVENT:
  1784.         g_fCBrkPressed  = TRUE;
  1785.         return TRUE;
  1786.  
  1787.     /* fatal events: shut down gracefully */
  1788.     case CTRL_CLOSE_EVENT:
  1789.     case CTRL_LOGOFF_EVENT:
  1790.     case CTRL_SHUTDOWN_EVENT:
  1791.         windgoto((int)Rows - 1, 0);
  1792.         sprintf((char *)IObuff, "Vim: Caught %s event\n",
  1793.                 (dwCtrlType == CTRL_CLOSE_EVENT ? "close"
  1794.                  : dwCtrlType == CTRL_LOGOFF_EVENT ? "logoff" : "shutdown"));
  1795.  
  1796. #ifdef DEBUG
  1797.         OutputDebugString(IObuff);
  1798. #endif /* DEBUG */
  1799.  
  1800.         preserve_exit();        /* output IObuff, preserve files and exit */
  1801.  
  1802.         return TRUE;            /* not reached */
  1803.  
  1804.     default:
  1805.         return FALSE;
  1806.     }
  1807. }
  1808.  
  1809.  
  1810. /*
  1811.  * set the tty in (raw) ? "raw" : "cooked" mode
  1812.  */
  1813.     void
  1814. mch_settmode(
  1815.     int raw)
  1816. {
  1817.     DWORD cmodein;
  1818.     DWORD cmodeout;
  1819.  
  1820.     GetConsoleMode(g_hConIn,  &cmodein);
  1821.     GetConsoleMode(g_hCurOut, &cmodeout);
  1822.  
  1823.     if (raw)
  1824.     {
  1825.         cmodein &= ~(ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT |
  1826.                      ENABLE_ECHO_INPUT);
  1827.         cmodein |= (
  1828. #ifdef USE_MOUSE
  1829.             (g_fMouseActive ? ENABLE_MOUSE_INPUT : 0) |
  1830. #endif /* USE_MOUSE */
  1831.             ENABLE_WINDOW_INPUT);
  1832.  
  1833.         SetConsoleMode(g_hConIn, cmodein);
  1834.  
  1835.         cmodeout &= ~(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
  1836.         SetConsoleMode(g_hCurOut, cmodeout);
  1837.         SetConsoleCtrlHandler(handler_routine, TRUE);
  1838.     }
  1839.     else /* cooked */
  1840.     {
  1841.         cmodein =  g_cmodein;
  1842.         cmodeout = g_cmodeout;
  1843.         SetConsoleMode(g_hConIn,  g_cmodein);
  1844.         SetConsoleMode(g_hCurOut, g_cmodeout);
  1845.  
  1846.         SetConsoleCtrlHandler(handler_routine, FALSE);
  1847.     }
  1848.  
  1849. #ifdef MCH_WRITE_DUMP
  1850.     if (fdDump)
  1851.     {
  1852.         fprintf(fdDump, "mch_settmode(%s, CurOut = %s, in = %x, out = %x)\n",
  1853.                 raw ? "raw" : "cooked",
  1854.                 (g_hCurOut == g_hSavOut ? "Sav" : "Con"),
  1855.                 cmodein, cmodeout);
  1856.         fflush(fdDump);
  1857.     }
  1858. #endif /* MCH_WRITE_DUMP */
  1859. }
  1860.  
  1861.  
  1862. /*
  1863.  * Get the size of the current window in `Rows' and `Columns'
  1864.  */
  1865.     int
  1866. mch_get_winsize()
  1867. {
  1868.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  1869.  
  1870.     if (GetConsoleScreenBufferInfo(g_hCurOut, &csbi))
  1871.     {
  1872.         Rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
  1873.         Columns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
  1874.     }
  1875.     else
  1876.     {
  1877.         Rows = 25;
  1878.         Columns = 80;
  1879.     }
  1880.  
  1881.     if (Columns < MIN_COLUMNS  ||  Rows < MIN_ROWS + 1)
  1882.     {
  1883.         /* these values are overwritten by termcap size or default */
  1884.         Rows = 25;
  1885.         Columns = 80;
  1886.     }
  1887.  
  1888.     check_winsize();
  1889.     set_scroll_region(0, 0, Columns - 1, Rows - 1);
  1890.  
  1891.     return OK;
  1892. }
  1893.  
  1894.  
  1895. /*
  1896.  * Set a console window to `xSize' * `ySize'
  1897.  */
  1898.     static void
  1899. ResizeConBufAndWindow(
  1900.     HANDLE hConsole,
  1901.     int xSize,
  1902.     int ySize)
  1903. {
  1904.     CONSOLE_SCREEN_BUFFER_INFO csbi;    /* hold current console buffer info */
  1905.     SMALL_RECT      srWindowRect;       /* hold the new console size */
  1906.     COORD           coordScreen;
  1907.     DWORD            dwErr = 0;
  1908.  
  1909. #ifdef MCH_WRITE_DUMP
  1910.     if (fdDump)
  1911.     {
  1912.         fprintf(fdDump, "ResizeConBufAndWindow(%d, %d)\n", xSize, ySize);
  1913.         fflush(fdDump);
  1914.     }
  1915. #endif /* MCH_WRITE_DUMP */
  1916.  
  1917.     GetConsoleScreenBufferInfo(hConsole, &csbi);
  1918.  
  1919.     /* get the largest size we can size the console window to */
  1920.     coordScreen = GetLargestConsoleWindowSize(hConsole);
  1921.  
  1922.     /* define the new console window size and scroll position */
  1923.     srWindowRect.Left = srWindowRect.Top = (SHORT) 0;
  1924.     srWindowRect.Right =  (SHORT) (min(xSize, coordScreen.X) - 1);
  1925.     srWindowRect.Bottom = (SHORT) (min(ySize, coordScreen.Y) - 1);
  1926.  
  1927.     /* define the new console buffer size */
  1928.     coordScreen.X = xSize;
  1929.     coordScreen.Y = ySize;
  1930.  
  1931.     if (!SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect))
  1932.     {
  1933. #ifdef MCH_WRITE_DUMP
  1934.         if (fdDump)
  1935.         {
  1936.             fprintf(fdDump, "SetConsoleWindowInfo failed: %lx\n",
  1937.                     GetLastError());
  1938.             fflush(fdDump);
  1939.         }
  1940. #endif /* MCH_WRITE_DUMP */
  1941.     }
  1942.  
  1943.     if (!SetConsoleScreenBufferSize(hConsole, coordScreen))
  1944.     {
  1945. #ifdef MCH_WRITE_DUMP
  1946.         if (fdDump)
  1947.         {
  1948.             fprintf(fdDump, "SetConsoleScreenBufferSize failed: %lx\n",
  1949.                     GetLastError());
  1950.             fflush(fdDump);
  1951.         }
  1952. #endif /* MCH_WRITE_DUMP */
  1953.     }
  1954.  
  1955. }
  1956.  
  1957.  
  1958. /*
  1959.  * Set the console window to `Rows' * `Columns'
  1960.  */
  1961.     void
  1962. mch_set_winsize()
  1963. {
  1964.     COORD coordScreen = GetLargestConsoleWindowSize(g_hCurOut);
  1965.  
  1966.     /* Clamp Rows and Columns to reasonable values */
  1967.     if (Rows > coordScreen.Y)
  1968.         Rows = coordScreen.Y;
  1969.     if (Columns > coordScreen.X)
  1970.         Columns = coordScreen.X;
  1971.  
  1972.     ResizeConBufAndWindow(g_hCurOut, Columns, Rows);
  1973.     set_scroll_region(0, 0, Columns - 1, Rows - 1);
  1974. }
  1975.  
  1976.  
  1977. /*
  1978.  * We have no job control, so fake it by starting a new shell.
  1979.  */
  1980.     void
  1981. mch_suspend()
  1982. {
  1983.     MSG_OUTSTR("new shell started\n");
  1984.     call_shell(NULL, SHELL_COOKED);
  1985.     need_check_timestamps = TRUE;
  1986. }
  1987.  
  1988.  
  1989. /*
  1990.  * Either execute a command by calling the shell or start a new shell
  1991.  */
  1992.     int
  1993. call_shell(
  1994.     char_u *cmd,
  1995.     int options)            /* SHELL_FILTER if called by do_filter() */
  1996.                             /* SHELL_COOKED if term needs cooked mode */
  1997.                             /* SHELL_EXPAND if called by ExpandWildCards() */
  1998. {
  1999.     int        x;
  2000.     int        stopped_termcap_mode = FALSE;
  2001.  
  2002.     flushbuf();
  2003.  
  2004.     /*
  2005.      * ALWAYS switch to non-termcap mode, otherwise ":r !ls" may crash.
  2006.      */
  2007.     if (g_hCurOut == g_hConOut)
  2008.     {
  2009.         termcap_mode_end();
  2010.         stopped_termcap_mode = TRUE;
  2011.     }
  2012.  
  2013. #ifdef MCH_WRITE_DUMP
  2014.     if (fdDump)
  2015.     {
  2016.         fprintf(fdDump, "call_shell(\"%s\", %d)\n", cmd, options);
  2017.         fflush(fdDump);
  2018.     }
  2019. #endif /* MCH_WRITE_DUMP */
  2020.  
  2021.     signal(SIGINT, SIG_IGN);    /* we don't want to be killed here by Ctrl-C*/
  2022.     signal(SIGBREAK, SIG_IGN);    /* Nor by Ctrl-Break */
  2023.  
  2024.     if (options & SHELL_COOKED)
  2025.         settmode(0);            /* set to cooked mode */
  2026.  
  2027.     if (cmd == NULL)
  2028.     {
  2029.         x = system(p_sh);
  2030.     }
  2031.     else
  2032.     {
  2033.         /* we use "command" or "cmd" to start the shell; slow but easy */
  2034.         char newcmd[CMDBUFFSIZE + 100];
  2035.  
  2036.         sprintf(newcmd, "%s /c %s", p_sh, cmd);
  2037.         x = system(newcmd);
  2038.     }
  2039.  
  2040.     settmode(1);                /* set to raw mode */
  2041.  
  2042.     if (x && !expand_interactively)
  2043.     {
  2044.         smsg("%d returned", x);
  2045.         msg_outchar('\n');
  2046.     }
  2047.     resettitle();
  2048.  
  2049.     signal(SIGINT, SIG_DFL);
  2050.     signal(SIGBREAK, SIG_DFL);
  2051.  
  2052.     if (stopped_termcap_mode)
  2053.         termcap_mode_start();
  2054.  
  2055.     return (x ? FAIL : OK);
  2056. }
  2057.  
  2058.  
  2059. #define FL_CHUNK 32
  2060.  
  2061. typedef struct filelist {
  2062.     char_u** file;
  2063.     int      nfiles;
  2064.     int      maxfiles;
  2065. } FileList;
  2066.  
  2067.  
  2068. /*
  2069.  * Add filename `f' to the list of files in `fl'
  2070.  */
  2071.     static void
  2072. addfile(
  2073.     FileList *fl,
  2074.     char *f,
  2075.     int isdir)
  2076. {
  2077.     char           *p, *pp;
  2078.  
  2079.     if (!fl->file)
  2080.     {
  2081.         fl->file = (char **) alloc(sizeof(char *) * FL_CHUNK);
  2082.         if (!fl->file)
  2083.             return;
  2084.         fl->nfiles = 0;
  2085.         fl->maxfiles = FL_CHUNK;
  2086.     }
  2087.     if (fl->nfiles >= fl->maxfiles)
  2088.     {
  2089.         char          **t;
  2090.         int             i;
  2091.  
  2092.         t = (char **) lalloc(sizeof(char *) * (fl->maxfiles + FL_CHUNK), TRUE);
  2093.         if (!t)
  2094.             return;
  2095.         for (i = fl->nfiles - 1; i >= 0; i--)
  2096.             t[i] = fl->file[i];
  2097.         vim_free(fl->file);
  2098.         fl->file = t;
  2099.         fl->maxfiles += FL_CHUNK;
  2100.     }
  2101.     p = alloc((unsigned) (strlen(f) + 1 + isdir));
  2102.     if (p)
  2103.     {
  2104.         /* replace slashes with backslashes while copying */
  2105.         for (pp = p; *f; ++f, ++pp)
  2106.         {
  2107.             if (*f == '/')
  2108.                 *pp = '\\';
  2109.             else
  2110.                 *pp = *f;
  2111.         }
  2112.         if (isdir)
  2113.             *pp++ = '\\';
  2114.         *pp = NUL;
  2115.     }
  2116.     fl->file[fl->nfiles++] = p;
  2117. }
  2118.  
  2119.  
  2120. /*
  2121.  * Does `s' contain a wildcard?
  2122.  */
  2123.     int
  2124. mch_has_wildcard(
  2125.     char_u *s)
  2126. {
  2127.     for ( ;  *s;  ++s)
  2128.         if (*s == '?' || *s == '*')
  2129.             return 1;
  2130.     return 0;
  2131. }
  2132.  
  2133.  
  2134. /*
  2135.  * Copy a string, forcing it to lowercase if DOWNCASE_FILENAMES is defined
  2136.  */
  2137. #ifdef DOWNCASE_FILENAMES
  2138.     static void
  2139. strlowcpy(
  2140.     char *d,
  2141.     char *s)
  2142. {
  2143.     while (*s)
  2144.         *d++ = tolower(*s++);
  2145.     *d = NUL;
  2146. }
  2147. #else /* !DOWNCASE_FILENAMES */
  2148. # define strlowcpy(d, s) STRCPY(d, s)
  2149. #endif /* !DOWNCASE_FILENAMES */
  2150.  
  2151.  
  2152. /*
  2153.  * comparison function for qsort in expandpath
  2154.  */
  2155.     static int
  2156. pstrcmp(
  2157.     const void *a,
  2158.     const void *b)
  2159. {
  2160. #ifdef DOWNCASE_FILENAMES
  2161.     return (stricmp(* (const char **) a, * (const char **) b));
  2162. #else /* !DOWNCASE_FILENAMES */
  2163.     return (strcmp(* (const char **) a, * (const char **) b));
  2164. #endif /* !DOWNCASE_FILENAMES */
  2165. }
  2166.  
  2167.  
  2168. /*
  2169.  * recursively build up a list of files in `fl' matching the first wildcard
  2170.  * in `path'.  `fonly' and `donly' are not used (files only and directories
  2171.  * only flags?).  If `notf' is set, we add `path' to `fl' even when no such
  2172.  * file exists.
  2173.  */
  2174.     static int
  2175. expandpath(
  2176.     FileList *fl,
  2177.     char *path,
  2178.     int fonly,
  2179.     int donly,
  2180.     int notf)
  2181. {
  2182.     char            buf[_MAX_PATH];
  2183.     char           *p,
  2184.                    *s,
  2185.                    *e;
  2186.     int             lastn,
  2187.                     c = 1,
  2188.                     r;
  2189.     WIN32_FIND_DATA fb;
  2190.     HANDLE          hFind;
  2191.     int                found_one = FALSE;
  2192.  
  2193.     lastn = fl->nfiles;
  2194.  
  2195.     /*
  2196.      * Find the first part in the path name that contains a wildcard.
  2197.      * Copy it into `buf', including the preceding characters.
  2198.      */
  2199.     p = buf;
  2200.     s = NULL;
  2201.     e = NULL;
  2202.     while (*path)
  2203.     {
  2204.         if (*path == '\\' || *path == ':' || *path == '/')
  2205.         {
  2206.             if (e)
  2207.                 break;
  2208.             else
  2209.                 s = p;
  2210.         }
  2211.         if (*path == '*' || *path == '?')
  2212.             e = p;
  2213.         *p++ = *path++;
  2214.     }
  2215.     e = p;
  2216.     if (s)
  2217.         s++;
  2218.     else
  2219.         s = buf;
  2220.  
  2221.     /* now we have one wildcard component between `s' and `e' */
  2222.     *e = NUL;
  2223.     r = 0;
  2224.  
  2225.     /* If we are expanding wildcards, we try both files and directories */
  2226.     if ((hFind = FindFirstFile(buf, &fb)) != INVALID_HANDLE_VALUE)
  2227.         while (c)
  2228.         {
  2229.             strlowcpy(s, fb.cFileName);
  2230.  
  2231.             /*
  2232.              * Ignore "." and "..".
  2233.              * When more follows, this must be a directory.
  2234.              */
  2235.             if ((s[0] != '.'  ||
  2236.                     (s[1] != NUL  &&  (s[1] != '.' || s[2] != NUL))) &&
  2237.                     (*path == NUL || mch_isdir(buf)))
  2238.             {
  2239.                 strcat(buf, path);
  2240.                 if (!mch_has_wildcard(path))
  2241.                     addfile(fl, buf, mch_isdir(buf));
  2242.                 else
  2243.                     r |= expandpath(fl, buf, fonly, donly, notf);
  2244.                 found_one = TRUE;
  2245.             }
  2246.  
  2247.             c = FindNextFile(hFind, &fb);
  2248.         }
  2249.  
  2250.     if (!found_one)
  2251.     {
  2252.         /* not found */
  2253.         STRCPY(e, path);
  2254.  
  2255.         if (notf)
  2256.             addfile(fl, buf, FALSE);
  2257.  
  2258.         return 1;               /* unexpanded or empty */
  2259.     }
  2260.  
  2261.     qsort(fl->file + lastn, fl->nfiles - lastn, sizeof(char *), pstrcmp);
  2262.     FindClose(hFind);
  2263.  
  2264.     return r;
  2265. }
  2266.  
  2267.  
  2268. /*
  2269.  * MSDOS rebuild of Scott Ballantyne's ExpandWildCards for amiga/arp. -- jw
  2270.  *
  2271.  * return OK for success, FAIL for error (you may lose some memory) and
  2272.  *       put an error message in *file.
  2273.  *
  2274.  * `num_pat' is number of input patterns
  2275.  * `pat' is array of pointers to input patterns
  2276.  * `num_file' is pointer to number of matched file names
  2277.  * `file' is pointer to array of pointers to matched file names
  2278.  * On Unix/Win32, we do not check for `files_only' yet
  2279.  * `list_notfound' is ignored
  2280.  */
  2281.     int
  2282. ExpandWildCards(
  2283.     int num_pat,
  2284.     char_u **pat,
  2285.     int *num_file,
  2286.     char_u ***file,
  2287.     int files_only,
  2288.     int list_notfound)
  2289. {
  2290.     int             i,
  2291.                     r = 0;
  2292.     FileList        f;
  2293.  
  2294.     f.file = NULL;
  2295.     f.nfiles = 0;
  2296.  
  2297.     for (i = 0; i < num_pat; i++)
  2298.     {
  2299.         if (!mch_has_wildcard(pat[i]))
  2300.             addfile(&f, pat[i], files_only ? FALSE : mch_isdir(pat[i]));
  2301.         else
  2302.             r |= expandpath(&f, pat[i], files_only, 0, list_notfound);
  2303.     }
  2304.  
  2305.     *num_file = f.nfiles;
  2306.     *file = (*num_file > 0) ? f.file : (char_u **)"";
  2307.  
  2308.     return (*num_file > 0) ? OK : FAIL;
  2309. }
  2310.  
  2311.  
  2312. #ifdef vim_chdir
  2313. # undef vim_chdir
  2314. #endif
  2315.  
  2316. /*
  2317.  * The normal _chdir() does not change the default drive.  This one does.
  2318.  * Returning 0 implies success; -1 implies failure.
  2319.  */
  2320.     int
  2321. vim_chdir(
  2322.     char *path)
  2323. {
  2324.     if (path[0] == NUL)         /* just checking... */
  2325.         return -1;
  2326.  
  2327.     if (isalpha(path[0])  &&  path[1] == ':')    /* has a drive name */
  2328.     {
  2329.         if (_chdrive(toupper(path[0]) - 'A' + 1) != 0)
  2330.             return -1;          /* invalid drive name */
  2331.         path += 2;
  2332.     }
  2333.  
  2334.     if (*path == NUL)           /* drive name only */
  2335.         return 0;
  2336.  
  2337.     return chdir(path);        /* let the normal chdir() do the rest */
  2338. }
  2339.  
  2340.  
  2341. /*
  2342.  * Copy the contents of screen buffer hSrc to the bottom-left corner
  2343.  * of screen buffer hDst.
  2344.  */
  2345.     static void
  2346. copy_screen_buffer_text(
  2347.     HANDLE hSrc,
  2348.     HANDLE hDst)
  2349. {
  2350.     int     i, j, nSrcWidth, nSrcHeight, nDstWidth, nDstHeight;
  2351.     COORD   coordOrigin;
  2352.     DWORD   dwDummy;
  2353.     LPSTR   pszOldText;
  2354.     CONSOLE_SCREEN_BUFFER_INFO csbiSrc, csbiDst;
  2355.  
  2356. #ifdef MCH_WRITE_DUMP
  2357.     if (fdDump)
  2358.     {
  2359.         fprintf(fdDump, "copy_screen_buffer_text(%s, %s)\n",
  2360.                 (hSrc == g_hSavOut ? "Sav" : "Con"),
  2361.                 (hDst == g_hSavOut ? "Sav" : "Con"));
  2362.         fflush(fdDump);
  2363.     }
  2364. #endif /* MCH_WRITE_DUMP */
  2365.  
  2366.     GetConsoleScreenBufferInfo(hSrc, &csbiSrc);
  2367.     nSrcWidth =  csbiSrc.srWindow.Right  - csbiSrc.srWindow.Left + 1;
  2368.     nSrcHeight = csbiSrc.srWindow.Bottom - csbiSrc.srWindow.Top  + 1;
  2369.  
  2370.     GetConsoleScreenBufferInfo(hDst, &csbiDst);
  2371.     nDstWidth =  csbiDst.srWindow.Right  - csbiDst.srWindow.Left + 1;
  2372.     nDstHeight = csbiDst.srWindow.Bottom - csbiDst.srWindow.Top  + 1;
  2373.  
  2374.     pszOldText = (LPSTR) alloc(nDstHeight * nDstWidth);
  2375.         
  2376.     /* clear the top few lines if Src shorter than Dst */
  2377.     for (i = 0;  i < nDstHeight - nSrcHeight;  i++)
  2378.     {
  2379.         for (j = 0;  j < nDstWidth;  j++)
  2380.             pszOldText[i * nDstWidth + j] = ' ';
  2381.     }
  2382.     
  2383.     /* Grab text off source screen. */
  2384.     coordOrigin.X = 0;
  2385.     coordOrigin.Y = (SHORT) max(0, csbiSrc.srWindow.Bottom + 1 - nDstHeight);
  2386.  
  2387.     for (i = 0;  i < min(nSrcHeight, nDstHeight);  i++)
  2388.     {
  2389.         LPSTR psz = pszOldText
  2390.                      + (i + max(0, nDstHeight - nSrcHeight)) * nDstWidth;
  2391.         
  2392.         ReadConsoleOutputCharacter(hSrc, psz, min(nDstWidth, nSrcWidth),
  2393.                                    coordOrigin, &dwDummy);
  2394.         coordOrigin.Y++;
  2395.         
  2396.         /* clear the last few columns if Src narrower than Dst */
  2397.         for (j = nSrcWidth;  j < nDstWidth;  j++)
  2398.             psz[j] = ' ';
  2399.     }
  2400.  
  2401.     /* paste Src's text onto Dst */
  2402.     coordOrigin.Y = csbiDst.srWindow.Top;
  2403.     WriteConsoleOutputCharacter(hDst, pszOldText, nDstHeight * nDstWidth,
  2404.                                 coordOrigin, &dwDummy);
  2405.     vim_free(pszOldText);
  2406. }
  2407.  
  2408.  
  2409. /* keep track of state of original console window */
  2410. static SMALL_RECT g_srOrigWindowRect;
  2411. static COORD      g_coordOrig;
  2412. static WORD       g_attrSave = 0;
  2413.  
  2414.  
  2415. /*
  2416.  * Start termcap mode by switching to our allocated screen buffer
  2417.  */
  2418.     static void
  2419. termcap_mode_start()
  2420. {
  2421.     CONSOLE_SCREEN_BUFFER_INFO csbi;
  2422.     DWORD dwDummy;
  2423.     COORD coord;
  2424.     WORD wAttr = (WORD) (g_attrSave ? g_attrSave : g_attrCurrent);
  2425.  
  2426.     GetConsoleScreenBufferInfo(g_hSavOut, &csbi);
  2427.     g_srOrigWindowRect = csbi.srWindow;
  2428.  
  2429.     g_coordOrig.X = 0;
  2430.     g_coordOrig.Y = csbi.dwCursorPosition.Y;
  2431.  
  2432.     if (g_hConOut == INVALID_HANDLE_VALUE)
  2433.     {
  2434.         /* Create a new screen buffer in which we do all of our editing.
  2435.          * This means we can restore the original screen when we finish. */
  2436.         g_hConOut = CreateConsoleScreenBuffer(GENERIC_WRITE | GENERIC_READ, 
  2437.                                               0, (LPSECURITY_ATTRIBUTES) NULL,
  2438.                                               CONSOLE_TEXTMODE_BUFFER,
  2439.                                               (LPVOID) NULL);
  2440.     }
  2441.  
  2442.     coord.X = coord.Y = 0;
  2443.     FillConsoleOutputCharacter(g_hConOut, ' ', Rows * Columns, coord, &dwDummy);
  2444.     FillConsoleOutputAttribute(g_hConOut, wAttr, Rows*Columns, coord, &dwDummy);
  2445.  
  2446.     copy_screen_buffer_text(g_hSavOut, g_hConOut);
  2447.     
  2448.     g_hCurOut = g_hConOut;
  2449.     SetConsoleActiveScreenBuffer(g_hCurOut);
  2450.  
  2451.     ResizeConBufAndWindow(g_hCurOut, Columns, Rows);
  2452.     set_scroll_region(0, 0, Columns - 1, Rows - 1);
  2453.     check_winsize();
  2454.  
  2455.     resettitle();
  2456.  
  2457.     textattr(wAttr);
  2458. }
  2459.  
  2460.  
  2461. /*
  2462.  * Turn off termcap mode by restoring the screen buffer we had upon startup
  2463.  */
  2464.     static void
  2465. termcap_mode_end()
  2466. {
  2467.     g_attrSave = g_attrCurrent;
  2468.  
  2469.     ResizeConBufAndWindow(g_hCurOut, g_nOldColumns, g_nOldRows);
  2470.     check_winsize();
  2471.  
  2472.     g_hCurOut = g_hSavOut;
  2473.     SetConsoleActiveScreenBuffer(g_hCurOut);
  2474.     SetConsoleCursorInfo(g_hCurOut, &g_cci);
  2475.  
  2476.     normvideo();
  2477.  
  2478.     if (!p_rs)
  2479.         g_coordOrig.Y = g_srOrigWindowRect.Bottom;
  2480.     
  2481.     SetConsoleCursorPosition(g_hCurOut, g_coordOrig);
  2482.  
  2483.     if (!p_rs)
  2484.         copy_screen_buffer_text(g_hConOut, g_hSavOut);
  2485.  
  2486.     clear_chars(g_coordOrig,
  2487.                 g_srOrigWindowRect.Right - g_srOrigWindowRect.Left + 1);
  2488.     
  2489.     mch_restore_title(3);
  2490.  
  2491.     SetConsoleMode(g_hConIn,  g_cmodein);
  2492.     SetConsoleMode(g_hSavOut, g_cmodeout);
  2493. }
  2494.  
  2495. /*
  2496.  * Switching off termcap mode is only allowed when Columns is 80, otherwise a
  2497.  * crash may result.  It's always allowed on NT.
  2498.  */
  2499.     int
  2500. can_end_termcap_mode(
  2501.     int give_msg)
  2502. {
  2503.     if (g_PlatformId == VER_PLATFORM_WIN32_NT  ||  Columns == 80)
  2504.         return TRUE;
  2505.     if (give_msg)
  2506.         msg("'columns' is not 80, cannot execute external commands");
  2507.     return FALSE;
  2508. }
  2509.  
  2510. /*
  2511.  * clear `n' chars, starting from `coord'
  2512.  */
  2513.     static void
  2514. clear_chars(
  2515.     COORD coord,
  2516.     DWORD n)
  2517. {
  2518.     DWORD dwDummy;
  2519.  
  2520.     FillConsoleOutputCharacter(g_hCurOut, ' ', n, coord, &dwDummy);
  2521.     FillConsoleOutputAttribute(g_hCurOut, g_attrCurrent, n, coord, &dwDummy);
  2522. }
  2523.  
  2524.  
  2525. /*
  2526.  * Clear the screen
  2527.  */
  2528.     static void
  2529. clear_screen()
  2530. {
  2531.     g_coord.X = g_coord.Y = 0;
  2532.     clear_chars(g_coord, Rows * Columns);
  2533. }
  2534.  
  2535.  
  2536. /*
  2537.  * Clear to end of display
  2538.  */
  2539.     static void
  2540. clear_to_end_of_display()
  2541. {
  2542.     clear_chars(g_coord, (Rows-g_coord.Y-1) * Columns + (Columns-g_coord.X));
  2543. }
  2544.  
  2545.  
  2546. /*
  2547.  * Clear to end of line
  2548.  */
  2549.     static void
  2550. clear_to_end_of_line()
  2551. {
  2552.     clear_chars(g_coord, Columns - g_coord.X);
  2553. }
  2554.  
  2555.  
  2556. /*
  2557.  * Scroll the scroll region up by `cLines' lines
  2558.  */
  2559.     static void
  2560. scroll(
  2561.     unsigned cLines)
  2562. {
  2563.     COORD oldcoord = g_coord;
  2564.     
  2565.     gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Top + 1);
  2566.     delete_lines(cLines);
  2567.  
  2568.     g_coord = oldcoord;
  2569. }
  2570.  
  2571.  
  2572. /*
  2573.  * Set the scroll region
  2574.  */
  2575.     static void
  2576. set_scroll_region(
  2577.     unsigned left,
  2578.     unsigned top,
  2579.     unsigned right,
  2580.     unsigned bottom)
  2581. {
  2582.     if (left >= right  ||  top >= bottom
  2583.         ||  right > (unsigned) Columns - 1
  2584.         ||  bottom > (unsigned) Rows - 1)
  2585.         return;
  2586.     
  2587.     g_srScrollRegion.Left =   left;
  2588.     g_srScrollRegion.Top =    top;
  2589.     g_srScrollRegion.Right =  right;
  2590.     g_srScrollRegion.Bottom = bottom;
  2591. }
  2592.  
  2593.  
  2594. /*
  2595.  * Insert `cLines' lines at the current cursor position
  2596.  */
  2597.     static void
  2598. insert_lines(
  2599.     unsigned cLines)
  2600. {
  2601.     SMALL_RECT      source;
  2602.     COORD           dest;
  2603.     CHAR_INFO       fill;
  2604.  
  2605.     dest.X = 0;
  2606.     dest.Y = g_coord.Y + cLines;
  2607.  
  2608.     source.Left   = 0;
  2609.     source.Top    = g_coord.Y;
  2610.     source.Right  = g_srScrollRegion.Right;
  2611.     source.Bottom = g_srScrollRegion.Bottom - cLines;
  2612.  
  2613.     fill.Char.AsciiChar = ' ';
  2614.     fill.Attributes = g_attrCurrent;
  2615.  
  2616.     ScrollConsoleScreenBuffer(g_hCurOut, &source, NULL, dest, &fill);
  2617.  
  2618.     /* Here we have to deal with a win32 console flake: If the scroll
  2619.      * region looks like abc and we scroll c to a and fill with d we get
  2620.      * cbd... if we scroll block c one line at a time to a, we get cdd...
  2621.      * vim expects cdd consistently... So we have to deal with that
  2622.      * here... (this also occurs scrolling the same way in the other
  2623.      * direction).  */
  2624.  
  2625.     if (source.Bottom < dest.Y)
  2626.     {
  2627.         COORD coord;
  2628.  
  2629.         coord.X = 0;
  2630.         coord.Y = source.Bottom;
  2631.         clear_chars(coord, Columns * (dest.Y - source.Bottom));
  2632.     }
  2633. }
  2634.  
  2635.  
  2636. /*
  2637.  * Delete `cLines' lines at the current cursor position
  2638.  */
  2639.     static void
  2640. delete_lines(
  2641.     unsigned cLines)
  2642. {
  2643.     SMALL_RECT      source;
  2644.     COORD           dest;
  2645.     CHAR_INFO       fill;
  2646.     int                nb;
  2647.  
  2648.     dest.X = 0;
  2649.     dest.Y = g_coord.Y;
  2650.  
  2651.     source.Left   = 0;
  2652.     source.Top    = g_coord.Y + cLines;
  2653.     source.Right  = g_srScrollRegion.Right;
  2654.     source.Bottom = g_srScrollRegion.Bottom;
  2655.  
  2656.     fill.Char.AsciiChar = ' ';
  2657.     fill.Attributes = g_attrCurrent;
  2658.  
  2659.     ScrollConsoleScreenBuffer(g_hCurOut, &source, NULL, dest, &fill);
  2660.  
  2661.     /* Here we have to deal with a win32 console flake: If the scroll
  2662.      * region looks like abc and we scroll c to a and fill with d we get
  2663.      * cbd... if we scroll block c one line at a time to a, we get cdd...
  2664.      * vim expects cdd consistently... So we have to deal with that
  2665.      * here... (this also occurs scrolling the same way in the other
  2666.      * direction).  */
  2667.  
  2668.     nb = dest.Y + (source.Bottom - source.Top) + 1;
  2669.  
  2670.     if (nb < source.Top)
  2671.     {
  2672.         COORD coord;
  2673.  
  2674.         coord.X = 0;
  2675.         coord.Y = nb;
  2676.         clear_chars(coord, Columns * (source.Top - nb));
  2677.     }
  2678. }
  2679.  
  2680.  
  2681. /*
  2682.  * Set the cursor position
  2683.  */
  2684.     static void
  2685. gotoxy(
  2686.     unsigned x,
  2687.     unsigned y)
  2688. {
  2689.     COORD coord2;
  2690.     
  2691.     if (x < 1  ||  x > (unsigned) Columns  ||  y < 1  ||  y > (unsigned) Rows)
  2692.         return;
  2693.  
  2694.     /* Should we check against g_srScrollRegion? */
  2695.  
  2696.     /* external cursor coords are 1-based; internal are 0-based */
  2697.     g_coord.X = coord2.X = x - 1;
  2698.     g_coord.Y = coord2.Y = y - 1;
  2699.  
  2700.     /* If we are using the window buffer that we had upon startup, make
  2701.      * sure to position cursor relative to the window upon that buffer */
  2702.     if (g_hCurOut == g_hSavOut)
  2703.     {
  2704.         CONSOLE_SCREEN_BUFFER_INFO csbi;
  2705.  
  2706.         GetConsoleScreenBufferInfo(g_hCurOut, &csbi);
  2707.         g_srOrigWindowRect = csbi.srWindow;
  2708.  
  2709.         coord2.X += (SHORT) (g_srOrigWindowRect.Left);
  2710.         coord2.Y += (SHORT) (g_srOrigWindowRect.Bottom + 1 - Rows);
  2711.     }
  2712.  
  2713.     SetConsoleCursorPosition(g_hCurOut, coord2);
  2714. }
  2715.  
  2716.  
  2717. /*
  2718.  * Set the current text attribute = (foreground | background)
  2719.  *
  2720.  * COLOR        FOREGROUND    BACKGROUND
  2721.  *  black            0            0
  2722.  *  blue            1           16
  2723.  *  green            2           32
  2724.  *  cyan            3           48
  2725.  *  red                4           64
  2726.  *  magenta            5           80
  2727.  *  brown            6           96
  2728.  *  lightgray        7          112
  2729.  *  darkgray        8          128
  2730.  *  lightblue        9          144
  2731.  *  lightgreen       10          160
  2732.  *  lightcyan       11          176
  2733.  *  lightred       12          192
  2734.  *  lightmagenta   13          208
  2735.  *  yellow           14          224
  2736.  *  white           15          240
  2737.  */
  2738.     static void
  2739. textattr(
  2740.     WORD wAttr)
  2741. {
  2742.     g_attrCurrent = wAttr;
  2743.  
  2744.     SetConsoleTextAttribute(g_hCurOut, wAttr);
  2745. }
  2746.  
  2747.  
  2748. /*
  2749.  * restore the default text attribute (whatever we started with)
  2750.  */
  2751.     static void
  2752. normvideo()
  2753. {
  2754.     textattr(g_attrDefault);
  2755. }
  2756.  
  2757.  
  2758. static WORD g_attrPreStandout = 0;
  2759.  
  2760. /*
  2761.  * Make the text standout, by brightening it
  2762.  */
  2763.     static void
  2764. standout()
  2765. {
  2766.     g_attrPreStandout = g_attrCurrent;
  2767.     textattr((WORD) (g_attrCurrent|FOREGROUND_INTENSITY|BACKGROUND_INTENSITY));
  2768. }
  2769.  
  2770.  
  2771. /*
  2772.  * Turn off standout mode
  2773.  */
  2774.     static void
  2775. standend()
  2776. {
  2777.     if (g_attrPreStandout)
  2778.     {
  2779.         textattr(g_attrPreStandout);
  2780.         g_attrPreStandout = 0;
  2781.     }
  2782. }
  2783.  
  2784.  
  2785. /*
  2786.  * visual bell: flash the screen
  2787.  */
  2788.     static void
  2789. visual_bell()
  2790. {
  2791.     COORD   coordOrigin = {0, 0};
  2792.     WORD    attrFlash = ~g_attrCurrent & 0xff;
  2793.     
  2794.     DWORD   dwDummy;
  2795.     LPWORD  oldattrs = (LPWORD) alloc(Rows * Columns * sizeof(WORD));
  2796.     
  2797.     ReadConsoleOutputAttribute(g_hCurOut, oldattrs, Rows * Columns,
  2798.                                coordOrigin, &dwDummy);
  2799.     FillConsoleOutputAttribute(g_hCurOut, attrFlash, Rows * Columns,
  2800.                                coordOrigin, &dwDummy);
  2801.  
  2802.     Sleep(15);        /* wait for 15 msec */
  2803.     WriteConsoleOutputAttribute(g_hCurOut, oldattrs, Rows * Columns,
  2804.                                  coordOrigin, &dwDummy);
  2805.     vim_free(oldattrs);
  2806. }
  2807.  
  2808.  
  2809. /*
  2810.  * Make the cursor visible or invisible
  2811.  */
  2812.     static void
  2813. cursor_visible(
  2814.     BOOL fVisible)
  2815. {
  2816.     CONSOLE_CURSOR_INFO cci;
  2817.  
  2818.     cci.bVisible = fVisible;
  2819.     /* make cursor big and visible (100 on Win95 makes it disappear)  */
  2820.     cci.dwSize = 99;           /* 100 percent cursor */
  2821.     SetConsoleCursorInfo(g_hCurOut, &cci);
  2822. }
  2823.  
  2824.  
  2825. /*
  2826.  * write `cchToWrite' characters in `pchBuf' to the screen
  2827.  */
  2828.     static BOOL
  2829. write_chars(
  2830.     LPCSTR pchBuf,
  2831.     DWORD  cchToWrite,
  2832.     DWORD* pcchWritten)
  2833. {
  2834.     BOOL f;
  2835.     COORD coord2 = g_coord;
  2836.  
  2837.     if (g_hCurOut == g_hSavOut)
  2838.     {
  2839.         CONSOLE_SCREEN_BUFFER_INFO csbi;
  2840.  
  2841.         GetConsoleScreenBufferInfo(g_hCurOut, &csbi);
  2842.  
  2843.         coord2.X += (SHORT) (csbi.srWindow.Left);
  2844.         coord2.Y += (SHORT) (csbi.srWindow.Bottom + 1 - Rows);
  2845.     }
  2846.  
  2847.     FillConsoleOutputAttribute(g_hCurOut, g_attrCurrent, cchToWrite,
  2848.                                coord2, pcchWritten);
  2849.     f = WriteConsoleOutputCharacter(g_hCurOut, pchBuf, cchToWrite,
  2850.                                     coord2, pcchWritten);
  2851.  
  2852.     g_coord.X += (SHORT) *pcchWritten;
  2853.  
  2854.     while (g_coord.X > g_srScrollRegion.Right)
  2855.     {
  2856.         g_coord.X -= (SHORT) Columns;
  2857.         if (g_coord.Y < g_srScrollRegion.Bottom)
  2858.             g_coord.Y++;
  2859.     }
  2860.  
  2861.     gotoxy(g_coord.X + 1, g_coord.Y + 1);
  2862.  
  2863.     return f;
  2864. }
  2865.  
  2866.  
  2867. /*
  2868.  * mch_write(): write the output buffer to the screen, translating ESC
  2869.  * sequences into calls to console output routines.
  2870.  */
  2871.     void
  2872. mch_write(
  2873.     char_u *s,
  2874.     int len)
  2875. {
  2876.     s[len] = NUL;
  2877.     
  2878.     if (! term_console)
  2879.     {
  2880.         write(1, s, (unsigned) len);
  2881.         return;
  2882.     }
  2883.  
  2884.     /* translate ESC | sequences into faked bios calls */
  2885.     while (len--)
  2886.     {
  2887.         /* optimization: use one single write_chars for runs of text,
  2888.          * rather than once per character  It ain't curses, but it helps. */
  2889.         
  2890.         DWORD  prefix = strcspn(s, "\n\r\b\a\033");
  2891.         
  2892.         if (p_wd)
  2893.         {
  2894.             WaitForChar(p_wd);
  2895.             if (prefix)
  2896.                 prefix = 1;
  2897.         }
  2898.  
  2899.         if (prefix)
  2900.         {
  2901.             DWORD nWritten;
  2902.             
  2903.             if (write_chars(s, prefix, &nWritten))
  2904.             {
  2905. #ifdef MCH_WRITE_DUMP
  2906.                 if (fdDump)
  2907.                 {
  2908.                     fputc('>', fdDump);
  2909.                     fwrite(s, sizeof(char_u), nWritten, fdDump);
  2910.                     fputs("<\n", fdDump);
  2911.                 }
  2912. #endif /* MCH_WRITE_DUMP */
  2913.                 len -= (nWritten - 1);
  2914.                 s += nWritten;
  2915.             }
  2916.         }
  2917.         else if (s[0] == '\n')
  2918.         {
  2919.             /* \n, newline: go to the beginning of the next line or scroll */
  2920.             if (g_coord.Y == g_srScrollRegion.Bottom)
  2921.             {
  2922.                 scroll(1);
  2923.                 gotoxy(g_srScrollRegion.Left + 1, g_srScrollRegion.Bottom + 1);
  2924.             }
  2925.             else
  2926.             {
  2927.                 gotoxy(g_srScrollRegion.Left + 1, g_coord.Y + 2);
  2928.             }
  2929. #ifdef MCH_WRITE_DUMP
  2930.             if (fdDump)
  2931.                 fputs("\\n\n", fdDump);
  2932. #endif /* MCH_WRITE_DUMP */
  2933.             s++;
  2934.         }
  2935.         else if (s[0] == '\r')
  2936.         {
  2937.             /* \r, carriage return: go to beginning of line */
  2938.             gotoxy(g_srScrollRegion.Left+1, g_coord.Y + 1);
  2939. #ifdef MCH_WRITE_DUMP
  2940.             if (fdDump)
  2941.                 fputs("\\r\n", fdDump);
  2942. #endif /* MCH_WRITE_DUMP */
  2943.             s++;
  2944.         }
  2945.         else if (s[0] == '\b')
  2946.         {
  2947.             /* \b, backspace: move cursor one position left */
  2948.             if (g_coord.X > g_srScrollRegion.Left)
  2949.                 g_coord.X--;
  2950.             else if (g_coord.Y > g_srScrollRegion.Top)
  2951.             {
  2952.                 g_coord.X = g_srScrollRegion.Right;
  2953.                 g_coord.Y--;
  2954.             }
  2955.             gotoxy(g_coord.X + 1, g_coord.Y + 1);
  2956. #ifdef MCH_WRITE_DUMP
  2957.             if (fdDump)
  2958.                 fputs("\\b\n", fdDump);
  2959. #endif /* MCH_WRITE_DUMP */
  2960.             s++;
  2961.         }
  2962.         else if (s[0] == '\a')
  2963.         {
  2964.             /* \a, bell */
  2965.             MessageBeep(0xFFFFFFFF);
  2966. #ifdef MCH_WRITE_DUMP
  2967.             if (fdDump)
  2968.                 fputs("\\a\n", fdDump);
  2969. #endif /* MCH_WRITE_DUMP */
  2970.             s++;
  2971.         }
  2972.         else if (s[0] == ESC  &&  len >= 3-1  &&  s[1] == '|')
  2973.         {
  2974. #ifdef MCH_WRITE_DUMP
  2975.             char_u* old_s = s;
  2976. #endif /* MCH_WRITE_DUMP */
  2977.             char_u* p;
  2978.             int arg1 = 0, arg2 = 0;
  2979.             
  2980.             switch (s[2])
  2981.             {
  2982.             /* one or two numeric arguments, separated by ';' */
  2983.  
  2984.             case '0': case '1': case '2': case '3': case '4':
  2985.             case '5': case '6': case '7': case '8': case '9':
  2986.                 p = s + 2;
  2987.                 arg1 = getdigits(&p);        /* no check for length! */
  2988.                 if (p > s + len)
  2989.                     break;
  2990.                 
  2991.                 if (*p == ';')
  2992.                 {
  2993.                     ++p;
  2994.                     arg2 = getdigits(&p);    /* no check for length! */
  2995.                     if (p > s + len)
  2996.                         break;
  2997.                     
  2998.                     if (*p == 'H')
  2999.                         gotoxy(arg2, arg1);
  3000.                     else if (*p == 'r')
  3001.                         set_scroll_region(0, arg1 - 1, Columns - 1, arg2 - 1);
  3002.                 }
  3003.                 else if (*p == 'A')
  3004.                 {
  3005.                     /* move cursor up arg1 lines in same column */
  3006.                     gotoxy(g_coord.X + 1,
  3007.                            max(g_srScrollRegion.Top, g_coord.Y - arg1) + 1);
  3008.                 }
  3009.                 else if (*p == 'C')
  3010.                 {
  3011.                     /* move cursor right arg1 columns in same line */
  3012.                     gotoxy(min(g_srScrollRegion.Right, g_coord.X + arg1) + 1,
  3013.                            g_coord.Y + 1);
  3014.                 }
  3015.                 else if (*p == 'H')
  3016.                 {
  3017.                     gotoxy(1, arg1);
  3018.                 }
  3019.                 else if (*p == 'L')
  3020.                 {
  3021.                     insert_lines(arg1);
  3022.                 }
  3023.                 else if (*p == 'm')
  3024.                 {
  3025.                     if (arg1 == 0)
  3026.                         normvideo();
  3027.                     else
  3028.                         textattr((WORD) arg1);
  3029.                 }
  3030.                 else if (*p == 'M')
  3031.                 {
  3032.                     delete_lines(arg1);
  3033.                 }
  3034.                 
  3035.                 len -= p - s;
  3036.                 s = p + 1;
  3037.                 break;
  3038.                 
  3039.  
  3040.             /* Three-character escape sequences */
  3041.                 
  3042.             case 'A':
  3043.                 /* move cursor up one line in same column */
  3044.                 gotoxy(g_coord.X + 1,
  3045.                        max(g_srScrollRegion.Top, g_coord.Y - 1) + 1);
  3046.                 goto got3;
  3047.  
  3048.             case 'B':
  3049.                 visual_bell();
  3050.                 goto got3;
  3051.                 
  3052.             case 'C':
  3053.                 /* move cursor right one column in same line */
  3054.                 gotoxy(min(g_srScrollRegion.Right, g_coord.X + 1) + 1,
  3055.                        g_coord.Y + 1);
  3056.                 goto got3;
  3057.  
  3058.             case 'E':
  3059.                 termcap_mode_end();
  3060.                 goto got3;
  3061.                 
  3062.             case 'F':
  3063.                 standout();
  3064.                 goto got3;
  3065.  
  3066.             case 'f':
  3067.                 standend();
  3068.                 goto got3;
  3069.  
  3070.             case 'H':
  3071.                 gotoxy(1, 1);
  3072.                 goto got3;
  3073.                 
  3074.             case 'j':
  3075.                 clear_to_end_of_display();
  3076.                 goto got3;
  3077.                 
  3078.             case 'J':
  3079.                 clear_screen();
  3080.                 goto got3;
  3081.                 
  3082.             case 'K':
  3083.                 clear_to_end_of_line();
  3084.                 goto got3;
  3085.                 
  3086.             case 'L':
  3087.                 insert_lines(1);
  3088.                 goto got3;
  3089.                 
  3090.             case 'M':
  3091.                 delete_lines(1);
  3092.                 goto got3;
  3093.                 
  3094.             case 'S':
  3095.                 termcap_mode_start();
  3096.                 goto got3;
  3097.                 
  3098.             case 'V':
  3099.                 cursor_visible(TRUE);
  3100.                 goto got3;
  3101.                 
  3102.             case 'v':
  3103.                 cursor_visible(FALSE);
  3104.                 goto got3;
  3105.  
  3106.             got3:
  3107.                 s += 3;
  3108.                 len -= 2;
  3109.             }
  3110.  
  3111. #ifdef MCH_WRITE_DUMP
  3112.             if (fdDump)
  3113.             {
  3114.                 fputs("ESC | ", fdDump);
  3115.                 fwrite(old_s + 2, sizeof(char_u), s - old_s - 2, fdDump);
  3116.                 fputc('\n', fdDump);
  3117.             }
  3118. #endif /* MCH_WRITE_DUMP */
  3119.         }
  3120.         else
  3121.         {
  3122.             /* Write a single character */
  3123.             DWORD nWritten;
  3124.  
  3125.             if (write_chars(s, 1, &nWritten))
  3126.             {
  3127. #ifdef MCH_WRITE_DUMP
  3128.                 if (fdDump)
  3129.                 {
  3130.                     fputc('>', fdDump);
  3131.                     fwrite(s, sizeof(char_u), nWritten, fdDump);
  3132.                     fputs("<\n", fdDump);
  3133.                 }
  3134. #endif /* MCH_WRITE_DUMP */
  3135.  
  3136.                 len -= (nWritten - 1);
  3137.                 s += nWritten;
  3138.             }
  3139.         }
  3140.     }
  3141.  
  3142. #ifdef MCH_WRITE_DUMP
  3143.     if (fdDump)
  3144.         fflush(fdDump);
  3145. #endif /* MCH_WRITE_DUMP */
  3146. }
  3147.  
  3148.  
  3149. /*
  3150.  * Delay for half a second.
  3151.  */
  3152.     void
  3153. mch_delay(
  3154.     long    msec,
  3155.     int        ignoreinput)
  3156. {
  3157.     if (ignoreinput)
  3158.         Sleep((int)msec);
  3159.     else
  3160.         WaitForChar(msec);
  3161. }
  3162.  
  3163.  
  3164. #ifdef vim_remove
  3165. # undef vim_remove
  3166. #endif
  3167.  
  3168. /*
  3169.  * this version of remove is not scared by a readonly (backup) file
  3170.  */
  3171.     int
  3172. vim_remove(
  3173.     char_u *name)
  3174. {
  3175.     SetFileAttributes(name, FILE_ATTRIBUTE_NORMAL);
  3176.     return DeleteFile(name) ? 0 : -1;
  3177. }
  3178.  
  3179.  
  3180. /*
  3181.  * check for an "interrupt signal": CTRL-break or CTRL-C
  3182.  */
  3183.     void
  3184. mch_breakcheck()
  3185. {
  3186.     if (g_fCtrlCPressed || g_fCBrkPressed)
  3187.     {
  3188.         g_fCtrlCPressed = g_fCBrkPressed = FALSE;
  3189.         got_int = TRUE;
  3190.     }
  3191. }
  3192.  
  3193.  
  3194. /*
  3195.  * How much memory is available?
  3196.  */
  3197.     long
  3198. mch_avail_mem(
  3199.     int special)
  3200. {
  3201.     return LONG_MAX;        /* virtual memory, eh? */
  3202. }
  3203.  
  3204.  
  3205. /*
  3206.  * return non-zero if a character is available
  3207.  */
  3208.     int
  3209. mch_char_avail()
  3210. {
  3211.     return WaitForChar(0L);
  3212. }
  3213.  
  3214.  
  3215. /*
  3216.  * set screen mode, always fails.
  3217.  */
  3218.     int
  3219. mch_screenmode(
  3220.     char_u *arg)
  3221. {
  3222.     EMSG("Screen mode setting not supported");
  3223.     return FAIL;
  3224. }
  3225.  
  3226.  
  3227. /*
  3228.  * win95rename works around a bug in rename (aka MoveFile) in
  3229.  * Windows 95: rename("foo.bar", "foo.bar~") will generate a
  3230.  * file whose shortfilename is "FOO.BAR" (its longfilename will
  3231.  * be correct: "foo.bar~").  Because a file can be accessed by
  3232.  * either its SFN or its LFN, "foo.bar" has effectively been
  3233.  * renamed to "foo.bar", which is not at all what was wanted.  This
  3234.  * seems to happen only when renaming files with three-character
  3235.  * extensions by appending a suffix that does not include ".".
  3236.  * Windows NT gets it right, however, with an SFN of "FOO~1.BAR".
  3237.  *
  3238.  * Like rename(), returns 0 upon success, non-zero upon failure.
  3239.  * Should probably set errno appropriately when errors occur.
  3240.  */
  3241.     int
  3242. win95rename(
  3243.     const char* pszOldFile,
  3244.     const char* pszNewFile)
  3245. {
  3246.     char szTempFile[_MAX_PATH];
  3247.     char szNewPath[_MAX_PATH];
  3248.     char* pszFilePart;
  3249.     HANDLE hf;
  3250.  
  3251. #undef rename
  3252.  
  3253.     /* get base path of new filename */
  3254.     if (GetFullPathName(pszNewFile, _MAX_PATH, szNewPath, &pszFilePart) == 0)
  3255.         return -1;
  3256.     else
  3257.         *pszFilePart = NUL;
  3258.     
  3259.     /* Get (and create) a unique temporary filename in directory of new file */
  3260.     if (GetTempFileName(szNewPath, "VIM", 0, szTempFile) == 0)
  3261.         return -2;
  3262.  
  3263.     /* blow the temp file away */
  3264.     if (! DeleteFile(szTempFile))
  3265.         return -3;
  3266.  
  3267.     /* rename old file to the temp file */
  3268.     if (! MoveFile(pszOldFile, szTempFile))
  3269.         return -4;
  3270.  
  3271.     /* now create an empty file called pszOldFile; this prevents
  3272.      * the operating system using pszOldFile as an alias (SFN)
  3273.      * if we're renaming within the same directory.  For example,
  3274.      * we're editing a file called filename.asc.txt by its SFN,
  3275.      * filena~1.txt.  If we rename filena~1.txt to filena~1.txt~
  3276.      * (i.e., we're making a backup while writing it), the SFN
  3277.      * for filena~1.txt~ will be filena~1.txt, by default, which
  3278.      * will cause all sorts of problems later in buf_write.  So, we
  3279.      * create an empty file called filena~1.txt and the system will have
  3280.      * to find some other SFN for filena~1.txt~, such as filena~2.txt
  3281.      */
  3282.     if ((hf = CreateFile(pszOldFile, GENERIC_WRITE, 0, NULL, CREATE_NEW,
  3283.                          FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
  3284.         return -5;
  3285.     if (! CloseHandle(hf))
  3286.         return -6;
  3287.     
  3288.     /* rename the temp file to the new file */
  3289.     if (! MoveFile(szTempFile, pszNewFile))
  3290.         return -7;
  3291.  
  3292.     /* finally, remove the empty old file */
  3293.     if (! DeleteFile(pszOldFile))
  3294.         return -8;
  3295.  
  3296.     return 0;    /* success */
  3297. }
  3298.  
  3299. /*
  3300.  * Special version of getenv(): use $HOME when $VIM not defined.
  3301.  */
  3302.     char_u *
  3303. vim_getenv(
  3304.     char_u *var)
  3305. {
  3306.     char_u    *retval;
  3307.  
  3308.     retval = (char_u *)getenv((char *)var);
  3309.  
  3310.     if (retval == NULL && STRCMP(var, "VIM") == 0)
  3311.         retval = (char_u *)getenv("HOME");
  3312.  
  3313. #ifdef MCH_WRITE_DUMP
  3314.     if (fdDump)
  3315.     {
  3316.         fprintf(fdDump, "$%s = \"%s\"\n", var, retval);
  3317.         fflush(fdDump);
  3318.     }
  3319. #endif
  3320.  
  3321.     return retval;
  3322. }
  3323.  
  3324.  
  3325. /*
  3326.  * Get the default shell for the current hardware platform
  3327.  */
  3328.     char*
  3329. default_shell()
  3330. {
  3331.     char* psz = NULL;
  3332.  
  3333.     PlatformId();
  3334.  
  3335.     if (g_PlatformId == VER_PLATFORM_WIN32_NT)            /* Windows NT */
  3336.         psz = "cmd.exe";
  3337.     else if (g_PlatformId == VER_PLATFORM_WIN32_WINDOWS)  /* Windows 95 */
  3338.         psz = "command.com";
  3339.  
  3340.     return psz;
  3341. }
  3342.  
  3343.  
  3344. /*
  3345.  * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
  3346.  */
  3347.     void
  3348. DumpPutS(
  3349.     const char* psz)
  3350. {
  3351. # ifdef MCH_WRITE_DUMP
  3352.     if (fdDump)
  3353.     {
  3354.         fputs(psz, fdDump);
  3355.         if (psz[strlen(psz) - 1] != '\n')
  3356.             fputc('\n', fdDump);
  3357.         fflush(fdDump);
  3358.     }
  3359. # endif /* MCH_WRITE_DUMP */
  3360. }
  3361.